<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>Hussein Al Hammad</title>
		<description>Personal blog of Hussein Al Hammad</description>
		<link>https://hussein-alhammad.com/blog</link>
		<atom:link href="https://hussein-alhammad.com/blog/rss.xml/" rel="self" type="application/rss+xml" />
		            <item>
	<title>AI vs Accessibility</title>
	<pubDate>Fri, 30 Jan 2026 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2026/01/ai-vs-accessibility/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2026/01/ai-vs-accessibility/</guid>
	<description><![CDATA[
		<p>You care about humans and want to make digital experiences accessible. Most businesses will prioritise whatever AI because it is the promised future.</p>

<p>Good. Take advantage of that. A big chunk of accessibility work is making things accessible to machines so they can then be accessible to humans.</p>

<p>Use their lingo. Call it machine-readable or AI-ready. That's how to make things AI-friendly 101.</p>

<p>Take stakeholders to the future they already want, but through the right route. Move accessibility from a compliance requirement to part of your AI strategy.</p>
	]]></description>
</item>            <item>
	<title>Expired email click tracking? There is a more resilient solution.</title>
	<pubDate>Mon, 26 Jan 2026 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2026/01/salesforce-expired-click-tracking/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2026/01/salesforce-expired-click-tracking/</guid>
	<description><![CDATA[
		<p>I wrote about how the use of the <a href="https://emailmarkup.org/en/blog/2024/ping-attribute-click-tracking/">ping attribute for click tracking in HTML emails</a> can be a more resilient for the email ecosystem.</p>

<p>On 23 January 2026, I received an email from Salesforce about a security issue that impacted click tracking:</p>

<blockquote>
  <p>Salesforce Security recently became aware of a security issue that affected links in emails sent through Marketing Cloud Engagement, including: Clicks, CloudPages, Forward to a Friend, Profile Center, Subscription Center, Unsub Center, and View as a Web Page.</p>
  
  <p>Prior to the fix, this issue—if exploited—could have allowed an unauthorized party to view or access data displayed on CloudPages and certain subscriber information, specifically for Forward to a Friend, Profile Center, Subscription Center, and Unsub Center. In addition, an unauthorized party could have potentially viewed emails sent through View as a Web Page.</p>
</blockquote>

<p>Salesforce's solution was to expire all affected links (included click tracking links) prior to 21 January 2026:</p>

<blockquote>
  <p>Out of an abundance of caution, links generated prior to January 21, 2026 at 23:00 UTC were expired at January 23, 2026 at 21:00 UTC: Clicks, CloudPages, Forward to a Friend, Profile Center, Subscription Center, Unsub Center, and View as a Web Page.</p>
</blockquote>

<p>While the ping anchor tag attribute would not have prevented the issue entirely, its adoption for click tracking in the email ecosystem could have reduced complexity, preserved link integrity, protected the recipient experience and limited business impact when failures occurred.</p>

<p>It's the difference between the solution being "all affected links are now expired" to "affected links still work but click tracking is disabled."</p>

<p>Function first. Measure second.</p>
	]]></description>
</item>            <item>
	<title>Mid Air</title>
	<pubDate>Sun, 25 May 2025 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2025/05/mid-air/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2025/05/mid-air/</guid>
	<description><![CDATA[
		<p>Working in a fast-paced, client-focused environment often means you have to keep the machine running at all times. There is little time to pause and evaluate. And even if there is general direction, you may have to go with the flow many times. You endure problems you know how to solve.</p>

<p>In such environments, change is hard. Change here is only possible if individuals go out of their way and fight inertia; not because the culture invests in change.</p>

<p>Change here happens mid air. It takes courageous individuals who are mad enough to touch the engine mid air. After all, this plane is not meant to land.</p>
	]]></description>
</item>            <item>
	<title>Deep Consumption</title>
	<pubDate>Sun, 02 Mar 2025 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2025/03/deep-consumption/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2025/03/deep-consumption/</guid>
	<description><![CDATA[
		<p>Many social media platforms serve small-sized content for you to get hooked on and consume more of. This is now a widely known thing and not at all ground breaking. You find yourself, and those around you, habitually wanting to consume more - regardless of what it is as long as it is short enough for their shotened attention span.</p>

<p>Working in tech can make this worse. The industry moves incredibly fast and it's natural to try to keep up with the changes. Afterall, your livelihood may depend on it.</p>

<p>Many in the industry end up consuming more. And many of those in tech are probably "optimisers", so they try to do it more efficiently. For those people, podcasts and videos are never played at 1x speed. The goal isn't just to consume more, but to do it faster too.</p>

<p>Given how much changes in the tech industry, there is a case for consuming more. The problem is when this habit creeps into other forms of consumption and learnings.</p>

<p>Allow yourself to ponder more deeply. Re-listen to that podcast episode that resonated with you. Re-read that book that made you think differently. Re-watch that talk. Think about them. Actually give your brain the chance to think thorugh and interrogate some ideas.</p>

<p>And most importantly, <strong>act on what you learned</strong>. Give it a try instead of being distracted by the next <em>learning</em> opportunity. Don't be simply a collector of knowledge. Mix what you already know with what you recently learned and start doing.</p>

<p>One thing you can try is creating a re-listen playlist and/or a re-read reading (books/articles) list. Next time you look for a new podcast episode or a new book, turn to these lists instead. Every now and then you can review these lists and ask yourself "what did I learn from this?", "what am I applying from this in my day-to-day?".</p>
	]]></description>
</item>            <item>
	<title>Whose user is it anyway?</title>
	<pubDate>Sun, 30 Jun 2024 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2024/06/whose-user-is-it-anyway/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2024/06/whose-user-is-it-anyway/</guid>
	<description><![CDATA[
		<p>In <a href="/blog/2021/10/am-i-third-party/">Wait.. Am I the third party?</a>, I referred to HTML email code as third party code. This is because the email client (first party) has to render code (HTML email message) written by a sender (the third party) for the recpient (second party - direct user of the app).</p>

<p>Why am I thinking about this again? Email messages may contain:</p>

<ul>
<li>dates</li>
<li>phone numbers</li>
<li>URLs</li>
<li>email addresses</li>
<li>physical addresses</li>
</ul>

<p>Some email clients choose to auto-link these if found in the email message. A date would link to the default calendar app on the device. A phone number would be a <code>tel</code> link. An email address would be a <code>mailto</code> link. And so on.</p>

<p>This seems reasonable. The app is making it easier for their direct user to perform actions with fewer steps. They can quickly add a bill's due date to their calendar. One click and the support phone number is on their dial screen. They don't have to copy that physical address manually and paste it on Google Maps. You get the picture.</p>

<p>But senders of email marketing messages do not like this. They think of the recpient as their user too/instead. They want to be able to control what their user sees and what parts of the email message they can interact with. They want to choose how links are styled. This also seems reasonable.</p>

<p>However, what I don't see as reasonable is when senders not only want to control how links are styled, but when they also want to contorl whether the email client auto-links any of these. I don't think this is a good goal. The email client is trying to provide a good user experience to the user enabling them to perform certain actions with fewer steps. I don't think it should be the third party's decision to say "no, don't do that" and break the consistency of the email client's UX across different email messages.</p>

<p>Senders choose to break the email client's preferred UX by <a href="/blog/2021/10/translatable-html-email/">making text less accessible</a> just so they can control the link style or prevent a piece of text from getting auto-linked.</p>

<p>On the other hand, email clients sometimes style links as if all email messages are only black-on-white (they aren't) and as if all languages can be styled the same (e.g. underline can impact legibility in languages like Arabic and Farsi).</p>

<p>And occasionally email clients would incorrectly auto-link things. Many social media handles include a dot <code>.</code> part way through and what comes after the dot can be a valid TLD. The email client may detect this handle as a valid domain and link to a site that does not exist or does not match what the sender is referring to.</p>

<p>Both the first party and the third party wants the user to belong to them. Each wants to control the UX. Each wants to control the styling. Each does things to cancel what the other does.</p>

<p>But, <em>whose user is it anyway?</em> That's irrelevent. The ultimate goal is to provide a good and accessible experience to the user. The way email clients and senders are handling this right now takes focus away from that goal and the engineering effort is instead put into tricking the other party's code to do what you want.</p>
	]]></description>
</item>            <item>
	<title>Render a Blade template from a Jigsaw event listener</title>
	<pubDate>Mon, 10 Jun 2024 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2024/06/render-blade-template-jigsaw-event/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2024/06/render-blade-template-jigsaw-event/</guid>
	<description><![CDATA[
		<p><a href="https://jigsaw.tighten.com/">Jigsaw</a> (a statitc site generator) has 3 <a href="https://jigsaw.tighten.com/docs/event-listeners/">events</a> you can listen to and run custom code when these events fire. One thing you may want to do in an event listener is render some HTML using a templating language. It makes sense to use Blade for this just like the rest of the project.</p>

<p>To do this, use <code>BladeCompiler</code> in <code>bootstrap.php</code> at the root directory of your Jigsaw project:</p>

<pre><code data-theme="material-theme-palenight" data-lang="php" class='language-php torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 1</span><span style="color: #89DDFF;">&lt;?php</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 2</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 3</span><span style="color: #F78C6C;">use</span><span style="color: #FFCB6B;"> </span><span style="color: #A6ACCD;">Illuminate</span><span style="color: #89DDFF;">\</span><span style="color: #A6ACCD;">View</span><span style="color: #89DDFF;">\</span><span style="color: #A6ACCD;">Compilers</span><span style="color: #89DDFF;">\</span><span style="color: #A6ACCD;">BladeCompiler</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 4</span><span style="color: #F78C6C;">use</span><span style="color: #FFCB6B;"> </span><span style="color: #A6ACCD;">TightenCo</span><span style="color: #89DDFF;">\</span><span style="color: #A6ACCD;">Jigsaw</span><span style="color: #89DDFF;">\</span><span style="color: #A6ACCD;">Jigsaw</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 5</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 6</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 7</span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">events</span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">afterBuild</span><span style="color: #89DDFF;">(</span><span style="color: #C792EA;">function</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">($</span><span style="color: #A6ACCD;">jigsaw</span><span style="color: #89DDFF;">)</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 8</span><span style="color: #89DDFF;">  </span><span style="color: #676E95;">// get the Blade template as a string</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 9</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">bladeTemplate </span><span style="color: #89DDFF;">=</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">jigsaw</span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">readSourceFile</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">path/to/template.blade.php</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">);</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">10</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">11</span><span style="color: #89DDFF;">  </span><span style="color: #676E95;">// render the template</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">12</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">renderedHTML </span><span style="color: #89DDFF;">=</span><span style="color: #A6ACCD;"> </span><span style="color: #FFCB6B;">BladeCompiler</span><span style="color: #89DDFF;">::</span><span style="color: #82AAFF;">render</span><span style="color: #89DDFF;">($</span><span style="color: #A6ACCD;">bladeTemplate</span><span style="color: #89DDFF;">,</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">[</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">13</span><span style="color: #89DDFF;">    </span><span style="color: #676E95;">// data to pass to the template</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">14</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">]);</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">15</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">16</span><span style="color: #89DDFF;">  </span><span style="color: #676E95;">// write the file (path relative to the build directory)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">17</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">jigsaw</span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">writeOutputFile</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">index.html</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">renderedHTML</span><span style="color: #89DDFF;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">18</span><span style="color: #89DDFF;">});</span></div></code></pre>

<p>You're not going to automatically get all of Jigsaw's global variables though. You can pass a <code>$page</code> object with all your global config variables available as properties (mimicking what Jigsaw does when rendering a Blade template):</p>

<pre><code data-theme="material-theme-palenight" data-lang="php" class='language-php torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">renderedHTML </span><span style="color: #89DDFF;">=</span><span style="color: #A6ACCD;"> </span><span style="color: #FFCB6B;">BladeCompiler</span><span style="color: #89DDFF;">::</span><span style="color: #82AAFF;">render</span><span style="color: #89DDFF;">($</span><span style="color: #A6ACCD;">bladeTemplate</span><span style="color: #89DDFF;">,</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">[</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">page</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">=&gt;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">jigsaw</span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">getConfig</span><span style="color: #89DDFF;">(),</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #89DDFF;">]);</span></div></code></pre>
	]]></description>
</item>            <item>
	<title>Using Alpine.js without setting directives</title>
	<pubDate>Tue, 19 Mar 2024 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2024/03/alpine-without-directives/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2024/03/alpine-without-directives/</guid>
	<description><![CDATA[
		<p>Tag directives are key to building Alpine.js components. However, Alpine is flexible enough that even if you don't have control over the HTML markup and cannot set directives directly on tags, you can still build Alpine components.</p>

<h2>Quick intro to Alpine directives</h2>

<p>An Alpine component must start with the <code>x-data</code> directive. Its value can be an inline object:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">x-data</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">{ isOpen: false }</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<p>Or you can use <code>Alpine.data()</code> and have a seperate (reusable) Alpine component object</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">x-data</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">myComponent</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 2</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 3</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 4</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">script</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 5</span><span style="color: #A6ACCD;">  document</span><span style="color: #89DDFF;">.</span><span style="color: #82AAFF;">addEventListener</span><span style="color: #A6ACCD;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">alpine:init</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">()</span><span style="color: #A6ACCD;"> </span><span style="color: #C792EA;">=&gt;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 6</span><span style="color: #F07178;">    </span><span style="color: #A6ACCD;">Alpine</span><span style="color: #89DDFF;">.</span><span style="color: #82AAFF;">data</span><span style="color: #F07178;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">myComponent</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span><span style="color: #F07178;"> </span><span style="color: #89DDFF;">()</span><span style="color: #F07178;"> </span><span style="color: #C792EA;">=&gt;</span><span style="color: #F07178;"> (</span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 7</span><span style="color: #F07178;">      isOpen</span><span style="color: #89DDFF;">:</span><span style="color: #F07178;"> </span><span style="color: #FF9CAC;">false</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 8</span><span style="color: #F07178;">    </span><span style="color: #89DDFF;">}</span><span style="color: #F07178;">))</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 9</span><span style="color: #F07178;">  </span><span style="color: #89DDFF;">}</span><span style="color: #A6ACCD;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">10</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">script</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<p>You can use inline directives:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">x-data</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">{ isOpen: false }</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">button</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">type</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">button</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">x-on:click</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">isOpen = !isOpen</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span><span style="color: #A6ACCD;">Toggle</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">button</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">x-show</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">isOpen</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span><span style="color: #A6ACCD;">    </span><span style="color: #676E95;">&lt;!--  --&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">6</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">7</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<p>Or encapsulate directives with <code>x-bind</code>:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">x-data</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">myComponent</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 2</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">button</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">type</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">button</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">x-bind</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">btn</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span><span style="color: #A6ACCD;">Toggle</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">button</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 3</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 4</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">x-bind</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">content</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 5</span><span style="color: #A6ACCD;">    </span><span style="color: #676E95;">&lt;!--  --&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 6</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 7</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 8</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 9</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">script</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">10</span><span style="color: #A6ACCD;">  document</span><span style="color: #89DDFF;">.</span><span style="color: #82AAFF;">addEventListener</span><span style="color: #A6ACCD;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">alpine:init</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">()</span><span style="color: #A6ACCD;"> </span><span style="color: #C792EA;">=&gt;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">11</span><span style="color: #F07178;">    </span><span style="color: #A6ACCD;">Alpine</span><span style="color: #89DDFF;">.</span><span style="color: #82AAFF;">data</span><span style="color: #F07178;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">myComponent</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span><span style="color: #F07178;"> </span><span style="color: #89DDFF;">()</span><span style="color: #F07178;"> </span><span style="color: #C792EA;">=&gt;</span><span style="color: #F07178;"> (</span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">12</span><span style="color: #F07178;">      isOpen</span><span style="color: #89DDFF;">:</span><span style="color: #F07178;"> </span><span style="color: #FF9CAC;">false</span><span style="color: #89DDFF;">,</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">13</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">14</span><span style="color: #F07178;">      btn</span><span style="color: #89DDFF;">:</span><span style="color: #F07178;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">15</span><span style="color: #F07178;">        [</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">x-on:click</span><span style="color: #89DDFF;">&#39;</span><span style="color: #F07178;">]</span><span style="color: #89DDFF;">()</span><span style="color: #F07178;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">16</span><span style="color: #F07178;">          </span><span style="color: #89DDFF;">this.</span><span style="color: #A6ACCD;">open</span><span style="color: #F07178;"> </span><span style="color: #89DDFF;">=</span><span style="color: #F07178;"> </span><span style="color: #89DDFF;">!this.</span><span style="color: #A6ACCD;">open</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">17</span><span style="color: #F07178;">        </span><span style="color: #89DDFF;">}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">18</span><span style="color: #F07178;">      </span><span style="color: #89DDFF;">},</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">19</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">20</span><span style="color: #F07178;">      content</span><span style="color: #89DDFF;">:</span><span style="color: #F07178;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">21</span><span style="color: #F07178;">        [</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">x-show</span><span style="color: #89DDFF;">&#39;</span><span style="color: #F07178;">]</span><span style="color: #89DDFF;">()</span><span style="color: #F07178;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">22</span><span style="color: #F07178;">          </span><span style="color: #89DDFF;">return</span><span style="color: #F07178;"> </span><span style="color: #89DDFF;">this.</span><span style="color: #A6ACCD;">open</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">23</span><span style="color: #F07178;">        </span><span style="color: #89DDFF;">}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">24</span><span style="color: #F07178;">      </span><span style="color: #89DDFF;">}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">25</span><span style="color: #F07178;">    </span><span style="color: #89DDFF;">}</span><span style="color: #F07178;">))</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">26</span><span style="color: #F07178;">  </span><span style="color: #89DDFF;">}</span><span style="color: #A6ACCD;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">27</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">script</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<h2>The directiveless approach</h2>

<p>When you don't have control over the HTML markup, you can programmatically set the directives.</p>

<p>While you can programmatically set all the directives with <code>element.setAttribute()</code>, it's probably not the cleanest or most maintainable approach especially for more complex components.</p>

<pre><code data-theme="material-theme-palenight" data-lang="js" class='language-js torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #A6ACCD;">element</span><span style="color: #89DDFF;">.</span><span style="color: #82AAFF;">setAttribute</span><span style="color: #A6ACCD;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">x-on:click</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">open = !open</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;">)</span></div></code></pre>

<p>Instead use <code>x-bind</code> to encapsulate directives so you only have to programmatically set <code>x-bind</code> on the elements you need.</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">class</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">container</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 2</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">button</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">type</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">button</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span><span style="color: #A6ACCD;">Toggle</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">button</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 3</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 4</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">class</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">content</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 5</span><span style="color: #A6ACCD;">    </span><span style="color: #676E95;">&lt;!--  --&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 6</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 7</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 8</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 9</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">script</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">10</span><span style="color: #89DDFF;">  </span><span style="color: #676E95;">// set directives programmatically</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">11</span><span style="color: #A6ACCD;">  document</span><span style="color: #89DDFF;">.</span><span style="color: #82AAFF;">querySelector</span><span style="color: #A6ACCD;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">.container</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">12</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">.</span><span style="color: #82AAFF;">setAttribute</span><span style="color: #A6ACCD;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">x-data</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">myComponent</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;">)</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">13</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">14</span><span style="color: #A6ACCD;">  document</span><span style="color: #89DDFF;">.</span><span style="color: #82AAFF;">querySelector</span><span style="color: #A6ACCD;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">.container button</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">15</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">.</span><span style="color: #82AAFF;">setAttribute</span><span style="color: #A6ACCD;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">x-bind</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">btn</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;">)</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">16</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">17</span><span style="color: #A6ACCD;">  document</span><span style="color: #89DDFF;">.</span><span style="color: #82AAFF;">querySelector</span><span style="color: #A6ACCD;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">.container .content</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">18</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">.</span><span style="color: #82AAFF;">setAttribute</span><span style="color: #A6ACCD;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">x-bind</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">content</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;">)</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">19</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">20</span><span style="color: #89DDFF;">  </span><span style="color: #676E95;">// the alpine component</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">21</span><span style="color: #A6ACCD;">  document</span><span style="color: #89DDFF;">.</span><span style="color: #82AAFF;">addEventListener</span><span style="color: #A6ACCD;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">alpine:init</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">()</span><span style="color: #A6ACCD;"> </span><span style="color: #C792EA;">=&gt;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">22</span><span style="color: #F07178;">    </span><span style="color: #A6ACCD;">Alpine</span><span style="color: #89DDFF;">.</span><span style="color: #82AAFF;">data</span><span style="color: #F07178;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">myComponent</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span><span style="color: #F07178;"> </span><span style="color: #89DDFF;">()</span><span style="color: #F07178;"> </span><span style="color: #C792EA;">=&gt;</span><span style="color: #F07178;"> (</span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">23</span><span style="color: #F07178;">      isOpen</span><span style="color: #89DDFF;">:</span><span style="color: #F07178;"> </span><span style="color: #FF9CAC;">false</span><span style="color: #89DDFF;">,</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">24</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">25</span><span style="color: #F07178;">      btn</span><span style="color: #89DDFF;">:</span><span style="color: #F07178;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">26</span><span style="color: #F07178;">        [</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">x-on:click</span><span style="color: #89DDFF;">&#39;</span><span style="color: #F07178;">]</span><span style="color: #89DDFF;">()</span><span style="color: #F07178;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">27</span><span style="color: #F07178;">          </span><span style="color: #89DDFF;">this.</span><span style="color: #A6ACCD;">open</span><span style="color: #F07178;"> </span><span style="color: #89DDFF;">=</span><span style="color: #F07178;"> </span><span style="color: #89DDFF;">!this.</span><span style="color: #A6ACCD;">open</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">28</span><span style="color: #F07178;">        </span><span style="color: #89DDFF;">}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">29</span><span style="color: #F07178;">      </span><span style="color: #89DDFF;">},</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">30</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">31</span><span style="color: #F07178;">      content</span><span style="color: #89DDFF;">:</span><span style="color: #F07178;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">32</span><span style="color: #F07178;">        [</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">x-show</span><span style="color: #89DDFF;">&#39;</span><span style="color: #F07178;">]</span><span style="color: #89DDFF;">()</span><span style="color: #F07178;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">33</span><span style="color: #F07178;">          </span><span style="color: #89DDFF;">return</span><span style="color: #F07178;"> </span><span style="color: #89DDFF;">this.</span><span style="color: #A6ACCD;">open</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">34</span><span style="color: #F07178;">        </span><span style="color: #89DDFF;">}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">35</span><span style="color: #F07178;">      </span><span style="color: #89DDFF;">}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">36</span><span style="color: #F07178;">    </span><span style="color: #89DDFF;">}</span><span style="color: #F07178;">))</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">37</span><span style="color: #F07178;">  </span><span style="color: #89DDFF;">}</span><span style="color: #A6ACCD;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">38</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">script</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>
	]]></description>
</item>            <item>
	<title>Use Statamic Blueprint to validate Collection Entry data</title>
	<pubDate>Sat, 13 Jan 2024 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2024/01/statamic-validate-with-blueprint/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2024/01/statamic-validate-with-blueprint/</guid>
	<description><![CDATA[
		<p>When programmatically creating or updating a Collection Entry, you can lean on the Collection’s Blueprint to help you validate the fields values:</p>

<pre><code data-theme="material-theme-palenight" data-lang="php" class='language-php torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 1</span><span style="color: #89DDFF;">&lt;?php</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 2</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 3</span><span style="color: #F78C6C;">use</span><span style="color: #FFCB6B;"> </span><span style="color: #A6ACCD;">Statamic</span><span style="color: #89DDFF;">\</span><span style="color: #A6ACCD;">Facades</span><span style="color: #89DDFF;">\</span><span style="color: #A6ACCD;">Blueprint</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 4</span><span style="color: #F78C6C;">use</span><span style="color: #FFCB6B;"> </span><span style="color: #A6ACCD;">Statamic</span><span style="color: #89DDFF;">\</span><span style="color: #A6ACCD;">Facades</span><span style="color: #89DDFF;">\</span><span style="color: #A6ACCD;">Entry</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 5</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 6</span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">data </span><span style="color: #89DDFF;">=</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">[</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 7</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">slug</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">=&gt;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">some-title</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 8</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">title</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">=&gt;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">Some title</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 9</span><span style="color: #89DDFF;">  </span><span style="color: #676E95;">// more data</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">10</span><span style="color: #89DDFF;">];</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">11</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">12</span><span style="color: #676E95;">// validate the data</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">13</span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">validated_data </span><span style="color: #89DDFF;">=</span><span style="color: #A6ACCD;"> </span><span style="color: #FFCB6B;">Blueprint</span><span style="color: #89DDFF;">::</span><span style="color: #82AAFF;">setDirectory</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">resources/blueprints/collections/my_collection</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">14</span><span style="color: #A6ACCD;">      </span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">find</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">my_collection</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">15</span><span style="color: #A6ACCD;">      </span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">fields</span><span style="color: #89DDFF;">()</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">16</span><span style="color: #A6ACCD;">      </span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">addValues</span><span style="color: #89DDFF;">($</span><span style="color: #A6ACCD;">data</span><span style="color: #89DDFF;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">17</span><span style="color: #A6ACCD;">      </span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">validator</span><span style="color: #89DDFF;">()-&gt;</span><span style="color: #82AAFF;">validate</span><span style="color: #89DDFF;">();</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">18</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">19</span><span style="color: #676E95;">// create &amp; save the entry with the validated data</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">20</span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">entry </span><span style="color: #89DDFF;">=</span><span style="color: #A6ACCD;"> </span><span style="color: #FFCB6B;">Entry</span><span style="color: #89DDFF;">::</span><span style="color: #82AAFF;">make</span><span style="color: #89DDFF;">()</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">21</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">collection</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">my_collection</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">22</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">slug</span><span style="color: #89DDFF;">(</span><span style="color: #82AAFF;">data_get</span><span style="color: #89DDFF;">($</span><span style="color: #A6ACCD;">validated_data</span><span style="color: #89DDFF;">,</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">slug</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">))</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">23</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">published</span><span style="color: #89DDFF;">()</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">24</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">data</span><span style="color: #89DDFF;">($</span><span style="color: #A6ACCD;">validated_data</span><span style="color: #89DDFF;">);</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">25</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">26</span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">entry</span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">save</span><span style="color: #89DDFF;">();</span></div></code></pre>
	]]></description>
</item>            <item>
	<title>Cascade Entry content into a routed Statamic Antlers view</title>
	<pubDate>Sat, 06 Jan 2024 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2024/01/cascade-content-routed-statamic-view/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2024/01/cascade-content-routed-statamic-view/</guid>
	<description><![CDATA[
		<p>Statamic handles collection entries routing for you - given the route is set in the <a href="https://statamic.dev/collections#routing">collection’s settings</a> (can be done via the control panel or directly in the collection’s YAML file).</p>

<p>When Statamic routes to a collection entry page, it <a href="https://statamic.dev/variables#overview">automatically</a> includes the entry variables in the view. You can access the entry’s title directly through the variable <code>title</code>. You don’t need to fetch the data in any way.</p>

<p>There are cases in which you need to create your own <a href="https://statamic.dev/routing">custom routes</a>, and your own <a href="https://statamic.dev/controllers">Laravel controllers</a> that return Antlers views.</p>

<pre><code data-theme="material-theme-palenight" data-lang="php" class='language-php torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #C792EA;">public</span><span style="color: #A6ACCD;"> </span><span style="color: #C792EA;">function</span><span style="color: #A6ACCD;"> </span><span style="color: #82AAFF;">index</span><span style="color: #89DDFF;">()</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">return</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">(</span><span style="color: #F78C6C;">new</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">\</span><span style="color: #A6ACCD;">Statamic</span><span style="color: #89DDFF;">\</span><span style="color: #A6ACCD;">View</span><span style="color: #89DDFF;">\</span><span style="color: #FFCB6B;">View</span><span style="color: #89DDFF;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #A6ACCD;">        </span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">template</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">myview</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span><span style="color: #A6ACCD;">        </span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">layout</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">mylayout</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">6</span><span style="color: #A6ACCD;">        </span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">with</span><span style="color: #89DDFF;">([</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">title</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">=&gt;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">Example Title</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">]);</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">7</span><span style="color: #89DDFF;">}</span></div></code></pre>

<p>You can add the variables you want to be accessible to you within the antlers view by passing an array to the <code>View::with()</code> method. But what do you do when you want to pass an entry’s variables to the view?</p>

<pre><code data-theme="material-theme-palenight" data-lang="php" class='language-php torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 1</span><span style="color: #F78C6C;">use</span><span style="color: #FFCB6B;"> </span><span style="color: #A6ACCD;">Statamic</span><span style="color: #89DDFF;">\</span><span style="color: #A6ACCD;">Facades</span><span style="color: #89DDFF;">\</span><span style="color: #A6ACCD;">Entry</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 2</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 3</span><span style="color: #C792EA;">public</span><span style="color: #A6ACCD;"> </span><span style="color: #C792EA;">function</span><span style="color: #A6ACCD;"> </span><span style="color: #82AAFF;">index</span><span style="color: #89DDFF;">()</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 4</span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 5</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">my_entry </span><span style="color: #89DDFF;">=</span><span style="color: #A6ACCD;"> </span><span style="color: #FFCB6B;">Entry</span><span style="color: #89DDFF;">::</span><span style="color: #82AAFF;">whereCollection</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">my_collection</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 6</span><span style="color: #A6ACCD;">                        </span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">where</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">slug</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">slug</span><span style="color: #89DDFF;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 7</span><span style="color: #A6ACCD;">                        </span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">where</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">published</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">true)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 8</span><span style="color: #A6ACCD;">                        </span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">first</span><span style="color: #89DDFF;">();</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 9</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">10</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">return</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">(</span><span style="color: #F78C6C;">new</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">\</span><span style="color: #A6ACCD;">Statamic</span><span style="color: #89DDFF;">\</span><span style="color: #A6ACCD;">View</span><span style="color: #89DDFF;">\</span><span style="color: #FFCB6B;">View</span><span style="color: #89DDFF;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">11</span><span style="color: #A6ACCD;">      </span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">template</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">myview</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">12</span><span style="color: #A6ACCD;">      </span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">layout</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">mylayout</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">13</span><span style="color: #A6ACCD;">      </span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">with</span><span style="color: #89DDFF;">([</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">14</span><span style="color: #89DDFF;">        </span><span style="color: #676E95;">// what do I add here?</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">15</span><span style="color: #A6ACCD;">      </span><span style="color: #89DDFF;">]);</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">16</span><span style="color: #89DDFF;">}</span></div></code></pre>

<p>You can use the <code>toArray()</code> method:</p>

<pre><code data-theme="material-theme-palenight" data-lang="php" class='language-php torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 1</span><span style="color: #89DDFF;">&lt;?php</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 2</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 3</span><span style="color: #F78C6C;">use</span><span style="color: #FFCB6B;"> </span><span style="color: #A6ACCD;">Statamic</span><span style="color: #89DDFF;">\</span><span style="color: #A6ACCD;">Facades</span><span style="color: #89DDFF;">\</span><span style="color: #A6ACCD;">Entry</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 4</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 5</span><span style="color: #C792EA;">public</span><span style="color: #A6ACCD;"> </span><span style="color: #C792EA;">function</span><span style="color: #A6ACCD;"> </span><span style="color: #82AAFF;">index</span><span style="color: #89DDFF;">()</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 6</span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 7</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">my_entry </span><span style="color: #89DDFF;">=</span><span style="color: #A6ACCD;"> </span><span style="color: #FFCB6B;">Entry</span><span style="color: #89DDFF;">::</span><span style="color: #82AAFF;">whereCollection</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">my_collection</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 8</span><span style="color: #A6ACCD;">                        </span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">where</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">slug</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">slug</span><span style="color: #89DDFF;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 9</span><span style="color: #A6ACCD;">                        </span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">where</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">published</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">true)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">10</span><span style="color: #A6ACCD;">                        </span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">first</span><span style="color: #89DDFF;">();</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">11</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">12</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">return</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">(</span><span style="color: #F78C6C;">new</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">\</span><span style="color: #A6ACCD;">Statamic</span><span style="color: #89DDFF;">\</span><span style="color: #A6ACCD;">View</span><span style="color: #89DDFF;">\</span><span style="color: #FFCB6B;">View</span><span style="color: #89DDFF;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">13</span><span style="color: #A6ACCD;">      </span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">template</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">myview</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">14</span><span style="color: #A6ACCD;">      </span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">layout</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">mylayout</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">15</span><span style="color: #A6ACCD;">      </span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">with</span><span style="color: #89DDFF;">($</span><span style="color: #A6ACCD;">my_entry</span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">toArray</span><span style="color: #89DDFF;">());</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">16</span><span style="color: #89DDFF;">}</span></div></code></pre>

<p>And you can add other variables too:</p>

<pre><code data-theme="material-theme-palenight" data-lang="php" class='language-php torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">return</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">(</span><span style="color: #F78C6C;">new</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">\</span><span style="color: #A6ACCD;">Statamic</span><span style="color: #89DDFF;">\</span><span style="color: #A6ACCD;">View</span><span style="color: #89DDFF;">\</span><span style="color: #FFCB6B;">View</span><span style="color: #89DDFF;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">template</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">myview</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">layout</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">mylayout</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">with</span><span style="color: #89DDFF;">(</span><span style="color: #82AAFF;">array_merge</span><span style="color: #89DDFF;">([</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span><span style="color: #89DDFF;">      </span><span style="color: #676E95;">// other variables</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">6</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">],</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">my_entry</span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">toArray</span><span style="color: #89DDFF;">()));</span></div></code></pre>

<p>However, you may run into unexpected templating issues with certain field types like the <a href="https://statamic.dev/fieldtypes/entries">Entries fieldtype</a> where can’t output the desired content even though you may be able to loop over the selected values for this field.</p>

<p>The better way to add your entry’s variables to the antlers view is by using the <code>View::cascadeContent()</code> method:</p>

<pre><code data-theme="material-theme-palenight" data-lang="php" class='language-php torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 1</span><span style="color: #89DDFF;">&lt;?php</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 2</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 3</span><span style="color: #F78C6C;">use</span><span style="color: #FFCB6B;"> </span><span style="color: #A6ACCD;">Statamic</span><span style="color: #89DDFF;">\</span><span style="color: #A6ACCD;">Facades</span><span style="color: #89DDFF;">\</span><span style="color: #A6ACCD;">Entry</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 4</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 5</span><span style="color: #C792EA;">public</span><span style="color: #A6ACCD;"> </span><span style="color: #C792EA;">function</span><span style="color: #A6ACCD;"> </span><span style="color: #82AAFF;">index</span><span style="color: #89DDFF;">()</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 6</span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 7</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">my_entry </span><span style="color: #89DDFF;">=</span><span style="color: #A6ACCD;"> </span><span style="color: #FFCB6B;">Entry</span><span style="color: #89DDFF;">::</span><span style="color: #82AAFF;">whereCollection</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">my_collection</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 8</span><span style="color: #A6ACCD;">                        </span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">where</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">slug</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">slug</span><span style="color: #89DDFF;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 9</span><span style="color: #A6ACCD;">                        </span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">where</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">published</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">true)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">10</span><span style="color: #A6ACCD;">                        </span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">first</span><span style="color: #89DDFF;">();</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">11</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">12</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">return</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">(</span><span style="color: #F78C6C;">new</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">\</span><span style="color: #A6ACCD;">Statamic</span><span style="color: #89DDFF;">\</span><span style="color: #A6ACCD;">View</span><span style="color: #89DDFF;">\</span><span style="color: #FFCB6B;">View</span><span style="color: #89DDFF;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">13</span><span style="color: #A6ACCD;">      </span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">template</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">myview</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">14</span><span style="color: #A6ACCD;">      </span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">layout</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">mylayout</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">15</span><span style="color: #A6ACCD;">      </span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">with</span><span style="color: #89DDFF;">([</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">16</span><span style="color: #89DDFF;">        </span><span style="color: #676E95;">// some variables</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">17</span><span style="color: #A6ACCD;">      </span><span style="color: #89DDFF;">])</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">18</span><span style="color: #A6ACCD;">      </span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">cascadeContent</span><span style="color: #89DDFF;">($</span><span style="color: #A6ACCD;">my_entry</span><span style="color: #89DDFF;">);</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">19</span><span style="color: #89DDFF;">}</span></div></code></pre>
	]]></description>
</item>            <item>
	<title>Minimal Jigsaw site</title>
	<pubDate>Sat, 08 Apr 2023 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2023/04/minimal-php-jigsaw/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2023/04/minimal-php-jigsaw/</guid>
	<description><![CDATA[
		<p>I like simple under-engineered solutions.</p>

<p>This site was built with Jigsaw a couple of years ago, but I didn't like that I was using both PHP and Node.js (to compile assets with Laravel Mix). I switched to an all Node.js solution (Astro) for about a year, but I missed the simplicity of Jigsaw in comparison.</p>

<p>So I switched back to Jigsaw, but decided I'd take a minimal approach this time with the below goals in mind:</p>

<ul>
<li>Zero NPM dependencies. I want the site to be solely compiled with PHP.</li>
<li>Single file components. I want to author the component's CSS in the same file I author the HTML. An approach I really liked in Astro.</li>
</ul>

<h2>Component CSS</h2>

<p>The goal is to be able to author CSS and HTML in the same component file</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">style</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">  </span><span style="color: #676E95;">/* my component CSS */</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">style</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">6</span><span style="color: #A6ACCD;">  </span><span style="color: #676E95;">&lt;!-- my component --&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">7</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<p>Using <a href="https://laravel.com/docs/10.x/blade#stacks">Blade's stacks</a>, the <code>&lt;style&gt;</code> element can be pushed to the <code>&lt;head&gt;</code>:</p>

<pre><code data-theme="material-theme-palenight" data-lang="blade" class='language-blade torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;!</span><span style="color: #F07178;">DOCTYPE</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">html</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">html</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">lang</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">en</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">head</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">@stack</span><span style="color: #A6ACCD;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">css</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">head</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">6</span><span style="color: #A6ACCD;">.</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">7</span><span style="color: #A6ACCD;">.</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">8</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">html</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<p>Using the <code>@pushOnce</code> directive, the <code>&lt;style&gt;</code> element is only pushed to the <code>&lt;head&gt;</code> once per page. It is also only pushed if the component is used on the page.</p>

<pre><code data-theme="material-theme-palenight" data-lang="blade" class='language-blade torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">@pushOnce</span><span style="color: #A6ACCD;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">css</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #89DDFF;">  &lt;</span><span style="color: #F07178;">style</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #A6ACCD;">    </span><span style="color: #676E95;">/* my component CSS */</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">style</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span><span style="color: #89DDFF;">@endPushOnce</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">6</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">7</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">8</span><span style="color: #A6ACCD;">  </span><span style="color: #676E95;">&lt;!-- my component --&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">9</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<p>For a light site the CSS doesn't need to be served via an external style sheet. So I am happy to keep the components' CSS in embedded <code>&lt;style&gt;</code> elements.</p>

<h2>Base CSS</h2>

<p>The base CSS is authored in a CSS file. So I could add it to the page via an external style sheet, but seeing how small it is I chose to follow the same approach as the components' CSS and embed it directly to the page using the <code>inline()</code> helper function:</p>

<pre><code data-theme="material-theme-palenight" data-lang="blade" class='language-blade torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 1</span><span style="color: #89DDFF;">&lt;!</span><span style="color: #F07178;">DOCTYPE</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">html</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 2</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">html</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">lang</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">en</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 3</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">head</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 4</span><span style="color: #89DDFF;">  &lt;</span><span style="color: #F07178;">style</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 5</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">{</span><span style="color: #A6ACCD;">{ inline(&#39;/assets/build/css/main.css&#39;) </span><span style="color: #89DDFF;">}</span><span style="color: #A6ACCD;">}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 6</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">style</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 7</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 8</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">@stack</span><span style="color: #A6ACCD;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">css</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 9</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">head</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">10</span><span style="color: #A6ACCD;">.</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">11</span><span style="color: #A6ACCD;">.</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">12</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">html</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<h2>CSS Minification</h2>

<p>Considering the way I chose to author the CSS, I decided to create a pair of <a href="https://jigsaw.tighten.com/docs/content-blade/#extending-blade-with-custom-directives">custom Blade directives</a> that minifies the CSS inside it using a PHP-based minifier (<a href="https://github.com/matthiasmullie/minify">matthiasmullie/minify</a>):</p>

<pre><code data-theme="material-theme-palenight" data-lang="blade" class='language-blade torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #82AAFF;">@minifyCSS</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #89DDFF;">  &lt;</span><span style="color: #F07178;">style</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #A6ACCD;">      </span><span style="color: #89DDFF;">{</span><span style="color: #A6ACCD;">{ inline(&#39;/assets/build/css/main.css&#39;) </span><span style="color: #89DDFF;">}</span><span style="color: #A6ACCD;">}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">style</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">6</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">@stack</span><span style="color: #A6ACCD;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">css</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">7</span><span style="color: #82AAFF;">@endMinifyCSS</span></div></code></pre>

<p><code>./blade.php</code>:</p>

<pre><code data-theme="material-theme-palenight" data-lang="php" class='language-php torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 1</span><span style="color: #89DDFF;">&lt;?php</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 2</span><span style="color: #676E95;">/** </span><span style="color: #F78C6C;">@var</span><span style="color: #676E95;"> </span><span style="color: #89DDFF;">\</span><span style="color: #A6ACCD;">Illuminate</span><span style="color: #89DDFF;">\</span><span style="color: #A6ACCD;">View</span><span style="color: #89DDFF;">\</span><span style="color: #A6ACCD;">Compilers</span><span style="color: #89DDFF;">\</span><span style="color: #FFCB6B;">BladeCompiler</span><span style="color: #676E95;"> $bladeCompiler */</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 3</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 4</span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">bladeCompiler</span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">directive</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">minifyCSS</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span><span style="color: #A6ACCD;"> </span><span style="color: #C792EA;">function</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">($</span><span style="color: #A6ACCD;">expression</span><span style="color: #89DDFF;">)</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 5</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">return</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">&lt;?php</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 6</span><span style="color: #C3E88D;">    if($page-&gt;minifyCSS) {</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 7</span><span style="color: #C3E88D;">      ob_start();</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 8</span><span style="color: #C3E88D;">    }</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 9</span><span style="color: #C3E88D;">  ?&gt;</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">10</span><span style="color: #89DDFF;">});</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">11</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">12</span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">bladeCompiler</span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">directive</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">endMinifyCSS</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span><span style="color: #A6ACCD;"> </span><span style="color: #C792EA;">function</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">($</span><span style="color: #A6ACCD;">expression</span><span style="color: #89DDFF;">)</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">13</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">return</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">&lt;?php</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">14</span><span style="color: #C3E88D;">    if($page-&gt;minifyCSS) {</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">15</span><span style="color: #C3E88D;">      $minifier = new MatthiasMullie\Minify\CSS();</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">16</span><span style="color: #C3E88D;">      $minifier-&gt;add(str_replace([&quot;&lt;style&gt;&quot;, &quot;&lt;/style&gt;&quot;], &quot;&quot;, ob_get_clean()));</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">17</span><span style="color: #C3E88D;">      echo &quot;&lt;style&gt;&quot; . $minifier-&gt;minify() . &quot;&lt;/style&gt;&quot;;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">18</span><span style="color: #C3E88D;">    }</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">19</span><span style="color: #C3E88D;">  ?&gt;</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">20</span><span style="color: #89DDFF;">});</span></div></code></pre>

<p>And in the configuration file of any environment where CSS needs to be minified, set <code>minifyCSS</code> to <code>true</code>:</p>

<pre><code data-theme="material-theme-palenight" data-lang="php" class='language-php torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">return</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">[</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">production</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">=&gt;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">true,</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">minifyCSS</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">=&gt;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">true,</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #89DDFF;">];</span></div></code></pre>

<h2>JavaScript</h2>

<p>Right now the site doesn't use any JS, but if I was to add any, I would use <a href="https://alpinejs.dev/">Alpine.js</a>. This would enable me to keep this NPM-free and continue with my single file component approach.</p>

<p>Plus I love Alpine!</p>

<h2>Syntax highlighting</h2>

<p>I blog about web dev so code syntax highlighting is a must. I offloaded the syntax highlighting to <a href="https://torchlight.dev/">Torchlight</a> which has <a href="https://torchlight.dev/docs/clients/jigsaw">first-party support</a> for Jigsaw.</p>

<h2>Putting it together</h2>

<h3>Example layout</h3>

<pre><code data-theme="material-theme-palenight" data-lang="blade" class='language-blade torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 1</span><span style="color: #89DDFF;">&lt;!</span><span style="color: #F07178;">DOCTYPE</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">html</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 2</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">html</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">lang</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #82AAFF;">{{</span><span style="color: #C3E88D;"> </span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">page</span><span style="color: #89DDFF;">-&gt;</span><span style="color: #A6ACCD;">language</span><span style="color: #C3E88D;"> ?? </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">en</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;"> </span><span style="color: #82AAFF;">}}</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 3</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">head</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 4</span><span style="color: #A6ACCD;">    </span><span style="color: #676E95;">{{-- meta tags --}}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 5</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 6</span><span style="color: #A6ACCD;">    </span><span style="color: #82AAFF;">@minifyCSS</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 7</span><span style="color: #89DDFF;">      &lt;</span><span style="color: #F07178;">style</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 8</span><span style="color: #A6ACCD;">          </span><span style="color: #89DDFF;">{</span><span style="color: #A6ACCD;">{ inline(&#39;/assets/build/css/main.css&#39;) </span><span style="color: #89DDFF;">}</span><span style="color: #A6ACCD;">}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 9</span><span style="color: #A6ACCD;">      </span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">style</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">10</span><span style="color: #A6ACCD;">      </span><span style="color: #89DDFF;">@stack</span><span style="color: #A6ACCD;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">css</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">11</span><span style="color: #A6ACCD;">    </span><span style="color: #82AAFF;">@endMinifyCSS</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">12</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">head</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">13</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">body</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">14</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">x-header</span><span style="color: #89DDFF;"> /&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">15</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">16</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">main</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">17</span><span style="color: #A6ACCD;">      </span><span style="color: #89DDFF;">@yield</span><span style="color: #A6ACCD;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">body</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">18</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">main</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">19</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">20</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">x-footer</span><span style="color: #89DDFF;"> /&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">21</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">body</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">22</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">html</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<h3>Example page</h3>

<pre><code data-theme="material-theme-palenight" data-lang="blade" class='language-blade torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 1</span><span style="color: #89DDFF;">@extends</span><span style="color: #A6ACCD;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">_layouts.main</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 2</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 3</span><span style="color: #89DDFF;">@pushOnce</span><span style="color: #A6ACCD;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">css</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 4</span><span style="color: #89DDFF;">  &lt;</span><span style="color: #F07178;">style</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 5</span><span style="color: #A6ACCD;">    </span><span style="color: #676E95;">/* page-specific CSS */</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 6</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">style</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 7</span><span style="color: #89DDFF;">@endPushOnce</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 8</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 9</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">10</span><span style="color: #89DDFF;">@section</span><span style="color: #A6ACCD;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">body</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">11</span><span style="color: #A6ACCD;">  </span><span style="color: #676E95;">{{-- page content example --}}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">12</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">13</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">section</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">14</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">h2</span><span style="color: #89DDFF;">&gt;</span><span style="color: #A6ACCD;">Latest posts</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">h2</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">15</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">16</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">@foreach</span><span style="color: #A6ACCD;">(</span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">content_posts</span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">take</span><span style="color: #89DDFF;">(</span><span style="color: #F78C6C;">5</span><span style="color: #89DDFF;">)</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">as</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">post)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">17</span><span style="color: #A6ACCD;">      </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">x-posts.card</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">:post</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">$post</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> /&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">18</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">@endforeach</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">19</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">20</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">a</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">href</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">/blog</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span><span style="color: #A6ACCD;">View all posts →</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">a</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">21</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">section</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">22</span><span style="color: #89DDFF;">@endsection</span></div></code></pre>

<h3>Example component</h3>

<pre><code data-theme="material-theme-palenight" data-lang="blade" class='language-blade torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">@pushOnce</span><span style="color: #A6ACCD;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">css</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;">)</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #89DDFF;">  &lt;</span><span style="color: #F07178;">style</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #A6ACCD;">    </span><span style="color: #676E95;">/* component CSS */</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">style</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span><span style="color: #89DDFF;">@endPushOnce</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">6</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">7</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">8</span><span style="color: #A6ACCD;">  </span><span style="color: #676E95;">&lt;!-- component --&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">9</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>
	]]></description>
</item>            <item>
	<title>Slide up/down animation with AlpineJS</title>
	<pubDate>Thu, 30 Mar 2023 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2023/03/alpine-slide-up-down-animation/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2023/03/alpine-slide-up-down-animation/</guid>
	<description><![CDATA[
		<p>With CSS it is not possible to transition the <code>height</code> property to <code>auto</code>:</p>

<pre><code data-theme="material-theme-palenight" data-lang="css" class='language-css torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">collapsible</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">height</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">0</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">overflow</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> hidden</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">transition</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> height </span><span style="color: #F78C6C;">.3s</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span><span style="color: #89DDFF;">}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">6</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">7</span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">collapsible</span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">is-open</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">8</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">height</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> auto</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">9</span><span style="color: #89DDFF;">}</span></div></code></pre>

<p>For years developers relied on jQuery's (or <a href="https://github.com/julianshapiro/velocity">Velocity</a>'s) <code>slideUp()</code> and <code>slideDown()</code> methods to perform slide up/down animations without setting a fixed height. In fact, I remember being brought into projects where jQuery was added to the project just for these 2 methods.</p>

<p>Here is how to implement slide up/down animation with Alpine JS.</p>

<h2>Component set up</h2>

<p>Start with an Alpine component with a property to manage the open state of a collapsible element. And let's add a <code>.collapsible</code> element.</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">x-data</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">{isOpen: false}</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">class</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">collapsible</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #A6ACCD;">    </span><span style="color: #676E95;">&lt;!-- your content --&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<h2>CSS</h2>

<p>Then we add the CSS styles for the collapsed state:</p>

<pre><code data-theme="material-theme-palenight" data-lang="css" class='language-css torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">collapsible</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">height</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">0</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">opacity</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">0</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">overflow</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> hidden</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">transition</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> all </span><span style="color: #F78C6C;">.4s</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">6</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">pointer-events</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> none</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">7</span><span style="color: #89DDFF;">}</span></div></code></pre>

<p>And then we add the CSS for the expanded state (excluding the <code>height</code> which we'll manage with Alpine):</p>

<pre><code data-theme="material-theme-palenight" data-lang="css" class='language-css torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">collapsible</span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">is-open</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">opacity</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">1</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">pointer-events</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> auto</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #89DDFF;">}</span></div></code></pre>

<h2>The magic</h2>

<p>What we want to do when the <code>isOpen</code> property is set to <code>true</code>:</p>

<ol>
<li>Add the <code>.is-open</code> class to the <code>.collapsible</code> element</li>
<li>Programmatically set a <code>height</code> to the <code>.collapsible</code> element</li>
</ol>

<p>For (1), we'll use <a href="https://alpinejs.dev/directives/bind#binding-classes">x-bind</a> to conditionally set a class based on the value of the <code>isOpen</code> property. This can be written as <code>x-bind:class</code> or <code>:class</code>:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">x-data</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">{isOpen: false}</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #89DDFF;">    </span><span style="color: #C792EA;">class</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">collapsible</span><span style="color: #89DDFF;">&quot;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #89DDFF;">    </span><span style="color: #C792EA;">:class</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">isOpen &amp;&amp; &#39;is-open&#39;</span><span style="color: #89DDFF;">&quot;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span><span style="color: #89DDFF;">  &gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">6</span><span style="color: #A6ACCD;">    </span><span style="color: #676E95;">&lt;!-- your content --&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">7</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">8</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<p>For (2), we'll also use <code>x-bind</code>, but to add some inline styles. We'll add the <code>height</code> property and give it a value we calculate with JS.</p>

<p>"Calculate" is a misleading term here. We are going to literally read the height of the element using <a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight">scrollHeight</a> which includes the height of an element's visible and invisible content. We'll access the element's DOM node using Alpine's <a href="https://alpinejs.dev/magics/el">$el</a> magic property.</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">x-data</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">{isOpen: false}</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #89DDFF;">    </span><span style="color: #C792EA;">class</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">collapsible</span><span style="color: #89DDFF;">&quot;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #89DDFF;">    </span><span style="color: #C792EA;">:class</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">isOpen &amp;&amp; &#39;is-open&#39;</span><span style="color: #89DDFF;">&quot;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span><span style="color: #89DDFF;">    </span><span style="color: #C792EA;">:style</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">isOpen &amp;&amp; {height: $el.scrollHeight+`px`}</span><span style="color: #89DDFF;">&quot;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">6</span><span style="color: #89DDFF;">  &gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">7</span><span style="color: #A6ACCD;">    </span><span style="color: #676E95;">&lt;!-- your content --&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">8</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">9</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<p>Here is the full demo including a button to expand/collapse the element:</p>

<p class="codepen" data-height="500" data-default-tab="result" data-slug-hash="JjZrQYV" data-user="hus_hmd" style="height: 500px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/hus_hmd/pen/JjZrQYV">
  AlpineJS: slide up/down animation</a> by Hussein Al Hammad (<a href="https://codepen.io/hus_hmd">@hus_hmd</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>

<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
	]]></description>
</item>            <item>
	<title>Reverse orphan word with CSS</title>
	<pubDate>Wed, 08 Mar 2023 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2023/03/reverse-orphan-word-css/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2023/03/reverse-orphan-word-css/</guid>
	<description><![CDATA[
		<p>An "orphan word" in typography refers to a word that sits alone on the last line of a sentence. I don't know if there's a known term of the reverse of this, but it is something I needed to enforce in past projects: <strong>the first word of a sentence to sit alone on the first line</strong>.</p>

<p>One way to achieve this is to add the line break <code>&lt;br&gt;</code> element after the first word:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">h1</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">  Lorem</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">br</span><span style="color: #89DDFF;">&gt;</span><span style="color: #A6ACCD;">ipsum dolor sit amet</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">h1</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<p>This is easy to do if you are hardcoding the content. It is certainly achievable with a scripting language if your content is dynamic, but there's also a simple and practical CSS approach:</p>

<ol>
<li>Target the first line of the text with the <code>::first-line</code> pseudo-element.</li>
<li>Set a large <code>word-spacing</code> that no two words can fit in a single line</li>
</ol>

<pre><code data-theme="material-theme-palenight" data-lang="css" class='language-css torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">reverse-orphan</span><span style="color: #89DDFF;">::</span><span style="color: #C792EA;">first-line</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">word-spacing</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">100vw</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #89DDFF;">}</span></div></code></pre>

<p>You can now apply specific styles to the first word by styling the first line:</p>

<pre><code data-theme="material-theme-palenight" data-lang="css" class='language-css torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">reverse-orphan</span><span style="color: #89DDFF;">::</span><span style="color: #C792EA;">first-line</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">  </span><span style="color: #676E95;">/* make it on its own */</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">word-spacing</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">100vw</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span><span style="color: #A6ACCD;">  </span><span style="color: #676E95;">/* style it how you wish */</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">6</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">text-transform</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> uppercase</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">7</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">font-family</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> monospace</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">8</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">color</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> tomato</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">9</span><span style="color: #89DDFF;">}</span></div></code></pre>

<p class="codepen" data-height="300" data-default-tab="css,result" data-slug-hash="poOWaVj" data-user="hus_hmd" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/hus_hmd/pen/poOWaVj">
  Reverse orphan word (alone on first line)</a> by Hussein Al Hammad (<a href="https://codepen.io/hus_hmd">@hus_hmd</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>

<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

<p>I realise there are multi-part names (and probably words) in some languages and you'd prefer to keep the full word on the first line. The above approach has no awareness of that; so use wisely.</p>
	]]></description>
</item>            <item>
	<title>Readable inlined CSS</title>
	<pubDate>Tue, 12 Jul 2022 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2022/07/readable-inlined-css/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2022/07/readable-inlined-css/</guid>
	<description><![CDATA[
		<p>Without judgement: there are many developers who manually inline CSS in their HTML emails. This post is aimed at developers who choose or have to manually inline their CSS.</p>

<p>If you’re reading this and not in the world of HTML email development, the short explanation is CSS has to be inlined out of necessity and not by choise.</p>

<p>I’m not going to dive into the maintainabiliy issues of inlined CSS and why you should avoid it if you can. What I want to discuss is the readability of inlined CSS. Readble code is very valuable; particularly in a team environment. If you don’t put an effort to keep your code readable, you’re hurting yourself and your team.</p>

<p>When you write CSS in a ruleset, you probably use whitespace to keep it readable like so:</p>

<pre><code data-theme="material-theme-palenight" data-lang="css" class='language-css torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">my-class</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">color</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">#</span><span style="color: #A6ACCD;">000</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #A6ACCD;">  fake-property</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> fake-value</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #89DDFF;">}</span></div></code></pre>

<p>Besides the indentation, you most likely add a space between the property name and the value:</p>

<pre><code data-theme="material-theme-palenight" data-lang="css" class='language-css torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #A6ACCD;">property: value;</span></div></code></pre>

<p>I see developers trying to do the same when manually inlining their CSS:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">style</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">color: #fff; property: value; fake-property: value;</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">style</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">color: #fff; property: value; fake-property: another-value; margin: 10px 0 30px; padding: 10px 20px;</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<p>The space between a property name and its value is equal in size to the space between a value and the following property. This makes it harder for you as a human to scan pairs. It simply takes your brain longer to parse the CSS.</p>

<p>A more readable alternative would be to stack the properties like you would when writing full rulesets:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">style</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #C3E88D;">  color: #fff;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #C3E88D;">  property: value;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #C3E88D;">  fake-property: value;</span><span style="color: #89DDFF;">&quot;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">6</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<p>I know this is not everyone’s cup of tea. It’s not the only approach though. You can keep everything in one line and omit the space between a property and its value:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">style</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">color:#fff; property:value; fake-property:value;</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<p>You end up with CSS that is a lot easier to scan and parse as a human. You can compare the readability of the two approaches below:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">style</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">color: #fff; property: value; fake-property: another-value; margin: 10px 0 30px; padding: 10px 20px;</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">style</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">color:#fff; property:value; fake-property:another-value; margin:10px 0 30px; padding:10px 20px;</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>
	]]></description>
</item>            <item>
	<title>Implicitly using the aspect-ratio CSS property on images in HTML emails</title>
	<pubDate>Fri, 29 Oct 2021 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2021/10/aspect-ratio-html-email/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2021/10/aspect-ratio-html-email/</guid>
	<description><![CDATA[
		<p>The <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/aspect-ratio">aspect-ratio CSS property</a> is supported across all major browsers now:</p>

<script src="https://cdn.jsdelivr.net/gh/ireade/caniuse-embed/public/caniuse-embed.min.js"></script>

<p class="ciu_embed" data-feature="mdn-css__properties__aspect-ratio" data-periods="future_1,current,past_1,past_2" data-accessible-colours="false">
  <p>Data on support for the mdn-css__properties__aspect-ratio feature across the major browsers</p>
</p>

<p>But most major email clients still do not support it:</p>

<iframe title="Can I email… aspect-ratio" src="https://embed.caniemail.com/css-aspect-ratio/" width="640" height="420" style="width:100%; max-width:40rem; height:26.25rem; border:none;" loading="lazy"></iframe>

<p>Yet, you can rely on it for images! Set the <code>width</code> and the <code>height</code> attributes on the <code>img</code> tag, and the browser handles the rest:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">img</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">src</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">https://via.placeholder.com/1200x675/FF0000/FFFFFF.png</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">alt</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">width</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">1200</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">height</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">675</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">style</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">width:100%; height:auto;</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> /&gt;</span></div></code></pre>

<p>This works because the browser's user agent style sheet utilises the values of the <code>width</code> and <code>height</code> attributes to generate the value for the <code>aspect-ratio</code> property:</p>

<pre><code data-theme="material-theme-palenight" data-lang="css" class='language-css torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #FFCB6B;">img</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">  aspect-ratio</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #82AAFF;">attr</span><span style="color: #89DDFF;">(</span><span style="color: #A6ACCD;">width</span><span style="color: #89DDFF;">)</span><span style="color: #A6ACCD;"> / </span><span style="color: #82AAFF;">attr</span><span style="color: #89DDFF;">(</span><span style="color: #A6ACCD;">height</span><span style="color: #89DDFF;">);</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #89DDFF;">}</span></div></code></pre>

<p>At the time of writing, the <code>attr()</code> function can only be used on the <code>content</code> CSS property. However, when it comes to the browsers UA style sheet the browser can map these values "under the hood" but the CSS value is still represented using the same <code>attr()</code> function.</p>

<p>If you're unfamiliar with the problems <code>aspect-ratio</code> solves, check:</p>

<ul>
<li>Jen Simmons's video: <a href="https://www.youtube.com/watch?v=4-d_SoCHeWE">Do This to Improve Image Loading on Your Website</a>.</li>
<li>Barry Pollard's article: <a href="https://www.smashingmagazine.com/2020/03/setting-height-width-images-important-again/">Setting Height And Width On Images Is Important Again</a></li>
</ul>

<h2>Web vs Email</h2>

<p>On a normal web page, it doesn't matter what the values for the <code>width</code> and <code>height</code> attributes are as long as the generated formula represent the same value. So for a <code>16:9</code> image, all the below are rendered the same way in modern browsers:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">img</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">src</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">https://via.placeholder.com/1200x675/FF0000/FFFFFF.png</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">alt</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">width</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">16</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">height</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">9</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">style</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">width:100%; height:auto;</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> /&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">img</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">src</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">https://via.placeholder.com/1200x675/FF0000/FFFFFF.png</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">alt</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">width</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">640</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">height</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">360</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">style</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">width:100%; height:auto;</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> /&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">img</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">src</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">https://via.placeholder.com/1200x675/FF0000/FFFFFF.png</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">alt</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">width</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">1200</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">height</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">675</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">style</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">width:100%; height:auto;</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> /&gt;</span></div></code></pre>

<p>Unfortunately, in email clients it's not as straightforward because:</p>

<ol>
<li>Outlook Windows still render HTML emails using Microsoft Word, not a browser engine.</li>
<li>Gmail's desktop basic HTML view rewrites the <code>height</code> property to <code>min-height</code>.</li>
</ol>

<p>So unlike on a normal page, it matters which values are used for the <code>width</code> and <code>height</code> attributes. Using the largest values at which you intend to render the image seems a safe approach. So if you have a <code>1200px x 676px</code> image, but the largest size at which the image is going to be rendered in your email is <code>640px x 360px</code>, you'd use:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">img</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">src</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">https://via.placeholder.com/1200x675/FF0000/FFFFFF.png</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">alt</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">width</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">640</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">height</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">360</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">style</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">width:100%; height:auto;</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> /&gt;</span></div></code></pre>
	]]></description>
</item>            <item>
	<title>The one-button interface</title>
	<pubDate>Fri, 22 Oct 2021 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2021/10/one-button-interface/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2021/10/one-button-interface/</guid>
	<description><![CDATA[
		<p>Recently I visited a sort of airsoft gun range. They had rooms with different set ups and challenges. In some of these rooms, they can run different types of games.</p>

<p>I was in one of these rooms and what caught my attention is how the operator selected and started the different games.. with a one-button interface. All he had is a yellow box with a single red button. He hits the button in different sequences to trigger different commands.</p>

<p>I initially thought that must be inconvenient. And the more I thought about it, the more I disliked it. I started thinking about how they must have had to program the controller to handle such commands. And the thoughts started snowballing from there. What is the on-going maintenance and the documentation of these commands like? What about onboarding new operators? That would be unnecessarily time consuming.</p>

<p>If it works for them, then that's good enough, right? What I consider inconvenient is irrelevant here. I'm glad I can easily interact with most software I use with more than just one button.</p>

<p>But what I considered inconvenient is actually necessary in a different context. What I described above is some sort of a <a href="https://en.wikipedia.org/wiki/Switch_access">single switch entry device</a>. People who are unable to use a mouse or keyboard need an alternative input device, and a single switch entry device is one example of assistive technology that can be used instead.</p>

<p>What I considered inconvenient to myself actually makes interacting with computers and smart phones possible for many.</p>
	]]></description>
</item>            <item>
	<title>Wait.. Am I the third party?</title>
	<pubDate>Thu, 21 Oct 2021 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2021/10/am-i-third-party/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2021/10/am-i-third-party/</guid>
	<description><![CDATA[
		<p>I used to write code for environments on which I had almost complete control. If there was third-party code running on these environments, it was because I (or the development team) explicitly allowed it to run. And that level of control felt good!</p>

<p>However, these days I author a lot of code that run in environments on which I have very little control. This feels frustrating at times. Adding client-side form validation on a HTML form that is already being validated with JS client side forces you to implement things in a certain way. And don't get me started on building a HTML email that gets rewritten in 100 ways by the different tech it touches it.</p>

<p>One day while having to implement a workaround after a workaround, I stopped. "Wait.. Am I the third party here?". It felt like I was. I guess I had an epiphany. This changed my mindset when approaching such tasks. I <em>am</em> the third party.</p>

<p><img src="/assets/images/third-party/captain-now.webp" alt="Look at me. I am the third party now." /></p>
	]]></description>
</item>            <item>
	<title>Is your HTML email translatable?</title>
	<pubDate>Sun, 03 Oct 2021 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2021/10/translatable-html-email/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2021/10/translatable-html-email/</guid>
	<description><![CDATA[
		<p>I've lived in 3 countries so far. And in all 3 countries I've encountered groups of people who cannot speak or read the local (or the most commonly-used) language.</p>

<p>Between new international students, foreign workers, immigrants and refugees there are many people who won't understand the content you have on your website, or the messages you send them via email.</p>

<p>I once was a new international student in Australia and understood very little English. Like many other international students at the time, I carried a portable translation device. Luckily now if you can connect to the internet, you can access a lot better translation services (e.g. Google Translate, Yandex Translate) for free! And you have the convenience of in-browser and in-email translations so you don't have to keep copy-pasting every bit of text you don't understand.</p>

<h2>Is translatability an accessibility concern?</h2>

<p>Absolutely! And translation software is one type of assistive technology. Making your HTML accessible to as many people (disabled or not) as possible should be your goal.</p>

<p>While it is not reasonable to translate and localise your emails for all languages or expect to know the actual preferred language for all your audience, it is reasonable for your audience to expect accessible emails from you!</p>

<h2>What makes your HTML email untranslatable?</h2>

<p>In the email industry some practices are favoured (despite accessibility concerns) in order to render emails in a certain way.</p>

<h3>Text on images</h3>

<p>Text on images has a number of accessibility concerns and translatability is one of them.</p>

<p>Gmail's built-in translator (Google Translate) translates the <code>alt</code> text of images, but the translated version is not visible. An average user will not be able to access the translated version.</p>

<p>Outlook's first-party <a href="https://support.microsoft.com/en-us/office/translator-for-outlook-3d7e12ed-99d6-406e-a453-b9db0d9653fa">Translator add-in</a> does not translate the <code>alt</code> text of images at all.</p>

<h3>Preventing auto-linking</h3>

<p>Another common practice in the email industry is to break words/phrases that trigger email clients to auto-link them with special characters like the "zero width no-break space" <code>&amp;#xfeff;</code> and the "zero width non-joiner" <code>&amp;#x200c;</code>:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">span</span><span style="color: #89DDFF;">&gt;</span><span style="color: #A6ACCD;">23 September</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">span</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">span</span><span style="color: #89DDFF;">&gt;</span><span style="color: #A6ACCD;">23 Se&amp;#xfeff;pte&amp;#xfeff;mber</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">span</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">span</span><span style="color: #89DDFF;">&gt;</span><span style="color: #A6ACCD;">23 Se&amp;#x200c;pte&amp;#x200c;mber</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">span</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<p>In Gmail, only the first <code>&lt;span&gt;</code> is correctly translated. In Outlook (using the first-party Translator add-in), only the last <code>&lt;span&gt;</code> is not correctly translated.</p>

<h3>VML</h3>

<p>Given Outlook Windows still uses Microsoft Word to render HTML emails in 2021, it can still render <a href="https://docs.microsoft.com/en-us/windows/win32/vml/web-workshop---specs---standards----introduction-to-vector-markup-language--vml-">VML</a> shapes. Outlook's own first-party Translator add-in does not translate text inside VML shapes. So that's <a href="https://github.com/hteumeuleu/email-bugs/issues/77">another accessibility issue</a> with VML shapes.</p>
	]]></description>
</item>            <item>
	<title>What to consider when testing CSS features in email</title>
	<pubDate>Fri, 01 Oct 2021 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2021/10/test-css-email-clients/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2021/10/test-css-email-clients/</guid>
	<description><![CDATA[
		<p>It's no secret that support of various CSS features is fragmented and widely inconsistent. But have you thought about what it takes in order to confidently say a feature is supported in an email client?</p>

<p>Let's go through the various technical aspects you need to consider when testing CSS in email.</p>

<h2>Browser support</h2>

<p>Let's start with the easy one. Most email clients rely on a browser to render HTML. This is obvious when it comes to webmail clients you access via a browser, but it is also true to native apps as they rely on browser engines to render the HTML. The exception here is Outlook Windows apps and Windows Mail as they use Microsoft Word to render the HTML.</p>

<p>Given browsers support for CSS features is not identical, the first factor we must consider when testing a CSS feature on email clients is whether browsers support it.</p>

<p>This is an important factor. On iOS, for instance, all native apps have no option but to use the same browser engine (WebKit) to render HTML. So up until iOS 14, no email client on iOS supported WebP images.</p>

<p>I am excluding Outlook Windows and Windows Mail apps from the rest of this article on purpose. Testing CSS features on email clients that use modern browser engines (which follow documented open standards) is one thing, and testing features on email clients that use proprietary software (which support a small set of HTML 4 features) is another thing.</p>

<h2>What "support" means</h2>

<p>So if the browser engine used by an email client supports certain CSS features, why do we need to look at whether that email client supports it too? How can they not support a feature if the browser engine they use support it?</p>

<p>For a number of reasons (security at the top of that list), blindly rendering third-party HTML code would be a huge issue. So email clients sanitise the code before rendering it to the user. An obvious and easy security win is the removal of any JavaScript code, but email clients tend to strip a lot more than that. They may strip (or even add!) certain certain CSS properties or values, HTML tags and some HTML attributes.</p>

<p>Just because Chrome supports a CSS property, it does not mean a webmail client running on Chrome supports it. CSS declarations in HTML emails can be:</p>

<ul>
<li>stripped out</li>
<li>overwritten

<ul>
<li>the CSS declaration is kept, but is overwritten by email client's own CSS</li>
<li>the CSS declaration is kept, but the email client's own CSS affects how the CSS property behaves</li>
</ul></li>
<li>rewritten

<ul>
<li>the CSS property is kept, but the value is changed</li>
<li>the value is kept, but the property is changed</li>
</ul></li>
</ul>

<p>And no, email clients do not document any of that.</p>

<h2>Narrower questions</h2>

<p>Broad questions are rarely helpful in the context of email clients. A question like "is flexbox/grid supported in email clients?" has no short answer.</p>

<p>Flexbox/grid are CSS layout modules. Figuring out <a href="https://caniuse.com/flexbox">whether the module is supported in a browser</a> is fairly straightforward. But you cannot check whether a whole CSS layout module is supported in email clients as easily despite the support summary below:</p>

<iframe title="Can I email… display:flex" src="https://embed.caniemail.com/css-display-flex/" width="640" height="420" style="width:100%; max-width:40rem; height:26.25rem; border:none;" loading="lazy"></iframe>

<p>The above summary tell us two things only: whether the CSS property <code>display</code> is supported and whether it can take the value of <code>flex</code>. Nothing more. It doesn't mean the flexbox layout module is fully supported.</p>

<p>So what sort of narrower questions can we ask?</p>

<h2>CSS properties</h2>

<p>Testing whether a CSS property is supported is a good first step.</p>

<p>Gmail does not support the <a href="https://www.caniemail.com/features/css-align-items/">align-items CSS property</a>. It completely removes it. So the following:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">style</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">display:flex; align-items:center;</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<p>becomes:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">style</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">display:flex;</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<p>And Yahoo does not support the <a href="https://www.caniemail.com/features/css-height/">height CSS property</a>. It keeps the value, but renames the property to <code>min-height</code>. So the following:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">style</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">height:100px;</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<p>becomes:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">style</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">min-height:100px;</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<p>And testing the property on different elements could be insightful. For instance, Outlook mobile apps support the <a href="https://www.caniemail.com/features/css-border-radius/">border-radius CSS property</a>, but their default CSS styles <a href="https://github.com/hteumeuleu/email-bugs/issues/98">prevent border-radius from working on tables</a>.</p>

<h2>CSS properties shorthand</h2>

<p>Email clients may also have different support for shorthand properties and the separate properties that can be set by a given shorthand property. The <a href="https://www.caniemail.com/features/css-background/">background CSS property</a> is an example of this.</p>

<h2>CSS values</h2>

<p>CSS properties are only half of the equation.</p>

<p>In a CSS declaration there is a CSS property and a CSS value. An email client may support a CSS property while not supporting all valid values the property accepts in the CSS spec.</p>

<p>How an email client handles an unsupported value varies. For example, with <code>vh</code> values, <a href="https://github.com/hteumeuleu/email-bugs/issues/94">Apple Mail on iOS 15 keeps the declaration, but rewrites values as <code>0</code></a>. While, Outlook mobile apps strip a declaration entirely if it has a <code>&lt;gradient&gt;</code> value type.</p>

<h2>CSS value types</h2>

<p>CSS has many value types (also known as "data types") such as <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/string"><code>&lt;string&gt;</code></a>, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/length"><code>&lt;length&gt;</code></a>, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/color"><code>&lt;color&gt;</code></a> and <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/image"><code>&lt;image&gt;</code></a>. And there are some sort of sub-types like <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/gradient"><code>&lt;gradient&gt;</code></a> which is a special type of <code>&lt;image&gt;</code>. CSS functional notations (such as <code>url()</code> and <code>calc()</code>) are technically value types too.</p>

<p>Multiple CSS properties can accept the same value types. An example is the <code>color</code> and <code>background-color</code> properties as both accept <code>&lt;color&gt;</code> values. And while <code>color</code> and <code>background-color</code> are widely supported among email clients, not all <code>&lt;color&gt;</code> values are.</p>

<p>Outlook.com, for example, supports the <code>rgb()</code> functional notation, but does not support the <code>hsl()</code> functional notation even though both are <code>&lt;color&gt;</code> values.</p>

<p>Similarly, <code>background-image</code> is another widely supported property only when the <code>url()</code> functional notation is used. Support for other <code>&lt;image&gt;</code> values is inconsistent.</p>

<h2>CSS value syntax</h2>

<p>A CSS value type can be written in more than one syntax. An email client may support a CSS value, but only in certain syntaxes.</p>

<p>Using the <code>rgb()</code> functional notation as an example. While it is widely supported, not all email clients support the whitespace syntax:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">style</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">background-color: rgb(0 128 0);</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">style</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">background-color: rgb(0 128 0 / 1);</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<p><code>border-radius</code> is a widely-supported property, but Yahoo does not support the slash notion for defining elliptical corners (using two radii):</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">style</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">border-radius: 10% / 50%;</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">style</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">border-radius: 27% 73% 70% 30% / 30% 34% 66% 70%;</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<h2>CSS value units</h2>

<p>Another thing to consider is the value units. An email client may generally support a CSS property like <code>height</code> which accepts <code>&lt;length&gt;</code> values according to the CSS spec, but the email client may not necessarily accept all <code>&lt;length&gt;</code> values. See: <a href="https://github.com/hteumeuleu/email-bugs/issues/94">Apple Mail on iOS 15 renders <code>vh</code> units as <code>0</code></a>.</p>

<h2>Side effects</h2>

<p>You may know a CSS feature is not supported in an email client, but still choose to use it as an enhancement for other email clients that do. This is a very reasonable thing to do (see <a href="https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement">Progressive Enhancement</a>).</p>

<p>However, it is important to check what an email client does when it encounters a CSS feature it doesn't support. Does it remove the whole CSS declaration? Does it rewrite the CSS property and/or value? Are there any side effects?</p>

<p>An example of a major side effect happens in Gmail. While Gmail supports the <code>rgb()</code> functional notation, it does not like it when the value is written in whitespace syntax <code>rgb(0 0 0)</code>. When Gmail encounters this syntax in a <code>style</code> attribute of an element, Gmail strips the whole attribute:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #676E95;">&lt;!-- This --&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">style</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">background-color:rgb(0 0 0); padding:20px;</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #676E95;">&lt;!-- Turns into this --&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<p>Guess what happens if you choose to include the whitespace syntax in your embedded CSS in a <code>&lt;style&gt;</code> block. It won't settle for removing the CSS declaration that includes <code>rgb()</code> with the whitespace syntax. It won't settle for the CSS ruleset where the value is used either. It removes the whole <code>&lt;style&gt;</code> block!</p>
	]]></description>
</item>            <item>
	<title>Arabic script on the Web</title>
	<pubDate>Sat, 21 Aug 2021 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2021/08/arabic-script/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2021/08/arabic-script/</guid>
	<description><![CDATA[
		<p>A few months ago I wondered whether it was possible to use CSS to control the positioning of <a href="https://en.wikipedia.org/wiki/Arabic_diacritics">diacritics</a> within Arabic script. This has led me to a very interesting read: <a href="https://www.w3.org/TR/alreq/">Text Layout Requirements for the Arabic Script</a> (W3C working draft).</p>

<p>Even as a native Arabic speaker, there are many points in that document (or other documents it links to) I had not really thought about. The <a href="https://www.w3.org/TR/alreq-gap/">Arabic and Persian Gap Analysis</a> also highlights gaps in the support for Arabic script on the web.</p>

<p>It is worth noting this affects more than just "websites". The document specifically says "on the Web and in eBooks". This also means in-browser and browser-based apps like Electron apps. The current gaps limit in-browser design apps in particular on how much they can support Arabic script (this doesn't excuse Figma for not supporting RTL/Arabic at all!).</p>

<h2>Unicode BIDI Algorithm</h2>

<p>An important piece in RTL text support in browsers is the <a href="https://www.w3.org/International/articles/inline-bidi-markup/uba-basics">Unicode Bidirectional algorithm</a>:</p>

<blockquote>
  <p>It is important to understand from the outset that, in all major web browsers, the order of characters in memory (logical) is not the same as the order in which they are displayed (visual).</p>
  
  <p>The set of rules applied by the browser to produce the correct order at the time of display are described by the Unicode Bidirectional Algorithm, or 'bidi algorithm' for short.</p>
</blockquote>

<p>If you ever worked on sentences containing a mix of both RTL-flowing and LTR-flowing characters, you know it is painful to author the content in many apps and it requires a bit of extra effort to get it to render ok in browsers. When you have control over the HTML, you can get the text flowing correctly. Having an understanding of how the BIDI algorithm works may help you to handle such sentences in HTML even if you don't read the languages. Understanding the algorithm can also be helpful in crafting sentences that render fine when you have no control over the HTML (e.g. subject line in email clients).</p>

<p>I'm not going to attempt to explain the algorithm here, but I'd like to highlight some interesting aspects.</p>

<p>Unicode characters can have different types: strong directional characters (e.g. alphabet characters), weak directional characters (e.g. common number separators) or neutral characters (e.g. whitespace). You can find a table containing examples of each type in <a href="http://www.unicode.org/reports/tr9/#Bidirectional_Character_Types">section 3.2 of this document</a>.</p>

<p>What's even more interesting is that some characters are logically interpreted in the Unicode Standard. The <code>U+0028</code> character (HTML entity: <code>&amp;#40;</code>, AKA: <code>(</code>) is not interpreted as "left parenthesis", but rather as "opening parenthesis". If you're familiar with <a href="/blog/2019/10/bidirectional-horizontal-rules-in-css/">CSS logical properties</a>, this concept of logical directions is not alien to you.</p>

<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="RwgbagJ" data-user="hus_hmd" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/hus_hmd/pen/RwgbagJ">
  Logical Unicode characters</a> by Hussein Al Hammad (<a href="https://codepen.io/hus_hmd">@hus_hmd</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>

<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
	]]></description>
</item>            <item>
	<title>The privacy concerns of the noscript land</title>
	<pubDate>Sat, 20 Feb 2021 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2021/02/privacy-concerns-of-noscript-land/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2021/02/privacy-concerns-of-noscript-land/</guid>
	<description><![CDATA[
		<p>I have recently learned that deferred loading of <code>&lt;img&gt;</code>s with the <code>loading</code> attribute only works if JavaScript is enabled.</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">img</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">loading</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">lazy</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">src</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">/image.jpg</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">alt</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;&quot;</span><span style="color: #89DDFF;"> /&gt;</span></div></code></pre>

<p>The <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-loading">MDN documentation</a> notes that this is an anti-tracking measure:</p>

<blockquote>
  <p>Loading is only deferred when JavaScript is enabled. This is an anti-tracking measure, because if a user agent supported lazy loading when scripting is disabled, it would still be possible for a site to track a user's approximate scroll position throughout a session, by strategically placing images in a page's markup such that a server can track how many images are requested and when.</p>
</blockquote>

<p>While the purpose of this behaviour seems reasonable on the surface, it made me thinks about two things:</p>

<h2>1. The goal of disabling JS</h2>

<p>This anti-tracking measure assumes that a user's goal of disabling JS is to prevent websites from tracking them and their actions on pages. This assumption justifies loading more resources on the user's device even if they do not scroll far enough down a page to need them.</p>

<p>I can't personally speak of the intention of disabling JS, but I am unsure how I feel about disabling the feature when JS is disabled in order to prevent tracking. After all, web features get maliciously used all the time.</p>

<p>Disabling deferred loading on <code>img[loading=lazy]</code> can have a direct impact on the user. I can see a user disabling JS in the browser to save on mobile data while browsing. They may not need to scroll all the way down to all pages. That is; they may not need to download all the images on all pages they visit. In this case, the anti-tracking measure backfires.</p>

<h2>2. The noscript tracking paths</h2>

<p>There are plenty of user-behaviour tracking approaches that work without JS.</p>

<h3>The loading attribute on iframe</h3>

<p>The <code>loading</code> attribute also works on <code>&lt;iframe&gt;</code>s. The current implementation of the <code>loading</code> attribute on <code>&lt;iframe&gt;</code> works even when JS is disabled. iFrame tracking pixels can be used to workaround the anti-tracking measure on images.</p>

<h3>The ping attribute on anchor tags</h3>

<p>The <code>ping</code> attribute on an anchor tag is typically used for tracking. It accepts a space-separated list of URLs to which the browser sends POST requests when the link is followed:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">a</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #89DDFF;">  </span><span style="color: #C792EA;">href</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">/contact</span><span style="color: #89DDFF;">&quot;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #89DDFF;">  </span><span style="color: #C792EA;">ping</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">https://example.com/my-tracker https://example.com/my-other-tracker</span><span style="color: #89DDFF;">&quot;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span><span style="color: #A6ACCD;">  Contact Us</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">6</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">a</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<p>The <code>ping</code> attribute works even when JS is disabled. It is <a href="https://caniuse.com/ping">disabled by default in Firefox</a> though.</p>

<h3>The noscript tag</h3>

<p>The <code>&lt;noscript&gt;</code> element can be used to conditionally load different/additional tracking pixels when JS is disabled:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">noscript</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">  </span><span style="color: #676E95;">&lt;!-- add tracking pixels here --&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">noscript</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<h3>The CSS approach</h3>

<p>CSS provides a number of ways to conditionally apply styles. It also provides a number of ways to load external resources such as images. So it is entirely possible to make use of this to track users without the need of JS.</p>

<p>Using media queries:</p>

<pre><code data-theme="material-theme-palenight" data-lang="css" class='language-css torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">@media</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">(</span><span style="color: #A6ACCD;">prefers-color-scheme</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> dark</span><span style="color: #89DDFF;">)</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">#</span><span style="color: #F78C6C;">tracker</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #A6ACCD;">    </span><span style="color: #B2CCD6;">background-image</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #82AAFF;">url</span><span style="color: #89DDFF;">(</span><span style="color: #A6ACCD;">/my-tracker.png</span><span style="color: #89DDFF;">);</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span><span style="color: #89DDFF;">}</span></div></code></pre>

<p>Using feature queries and browser-specific prefixed CSS properties:</p>

<pre><code data-theme="material-theme-palenight" data-lang="css" class='language-css torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">@supports</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">(</span><span style="color: #B2CCD6;">-webkit-overflow-scrolling</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> touch</span><span style="color: #89DDFF;">)</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">#</span><span style="color: #F78C6C;">tracker</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #A6ACCD;">    </span><span style="color: #B2CCD6;">background-image</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #82AAFF;">url</span><span style="color: #89DDFF;">(</span><span style="color: #A6ACCD;">/my-tracker.png</span><span style="color: #89DDFF;">);</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span><span style="color: #89DDFF;">}</span></div></code></pre>

<p>CSS also provides a number of ways to apply styles based on user interactions. For example, you can track some user actions in a similar manner to the above using <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes">pseudo-classes</a>. And I'm sure there are more novel ways to make use of other CSS features for tracking.</p>

<h2>Final thoughts</h2>

<p>Built-in anti-tracking measures is a very nice goal in theory, but it seems very hard to implement without sacrificing something else in return.</p>

<p>So this begs the question: is "whether JS is enabled in the browser" the right criterion for enforcing built-in anti-tracking measures?</p>
	]]></description>
</item>            <item>
	<title>Add RTL formatting to GitHub markdown</title>
	<pubDate>Tue, 16 Feb 2021 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2021/02/rtl-github-markdown/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2021/02/rtl-github-markdown/</guid>
	<description><![CDATA[
		<p>Recently Abdelhadi Jaffar has translated the Alpine.js documentation to Arabic, and it was added among the other translations on the <a href="https://github.com/alpinejs/alpine">Alpine.js GitHub repository</a>, all of which are Markdown files so they are nicely rendered and can be read directly on GitHub.</p>

<p>Arabic is written right to left, but GitHub's layout is LTR. This made the Arabic documentation hard to read on GitHub. Fortunately, we can add HTML to Markdown files. I opened a PR (which has been merged) to sprinkle a bit of HTML to change the layout to RTL.</p>

<h2>How?</h2>

<p>Wrap the whole thing in <code>div[dir="rtl"]</code>:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">dir</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">rtl</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;"># العنوان</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #A6ACCD;">ها نحن ذا ، على دروب كنزنا. نسير معاً و أمالنا تسير قبلنا. من غيرنا؟ يقطع درباً مثلنا! درباً خطيرة إلى الجزيرة ، من غيرنا؟</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<p>Wrap code blocks in <code>div[dir="ltr"]</code> (assuming your code is written in a LTR language!):</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">dir</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">rtl</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 2</span><span style="color: #A6ACCD;"># العنوان</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 3</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 4</span><span style="color: #A6ACCD;">ها نحن ذا ، على دروب كنزنا. نسير معاً و أمالنا تسير قبلنا. من غيرنا؟ يقطع درباً مثلنا! درباً خطيرة إلى الجزيرة ، من غيرنا؟</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 5</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 6</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">dir</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">ltr</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 7</span><span style="color: #A6ACCD;">```js</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 8</span><span style="color: #A6ACCD;">import &#39;alpinejs&#39;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 9</span><span style="color: #A6ACCD;">```</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">10</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">11</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">12</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<p>If you have LTR copy/code within RTL copy, it may be a good practice to default to wrapping it in <code>span[dir="ltr"]</code> as some characters may flip to the wrong side. For instance, the brackets <code>()</code> of a function can appear on the left instead the right e.g. <span dir="rtl"><code>myFunction()</code></span>:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">dir</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">rtl</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;"># العنوان</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #A6ACCD;">إذا أضفنا </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">span</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">dir</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">ltr</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span><span style="color: #A6ACCD;">`.passive`</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">span</span><span style="color: #89DDFF;">&gt;</span><span style="color: #A6ACCD;"> إلى المتصنّت للحدث، فإن هذا الرمز المميز سيعطل وظيفة </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">span</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">dir</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">ltr</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span><span style="color: #A6ACCD;">`preventDefault()`</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">span</span><span style="color: #89DDFF;">&gt;</span><span style="color: #A6ACCD;"> ولن تعمل على أي حدث يتم تنفيذه. يمكن أن يساعدك أحياناً في تحسين أداء التمرير (scroll) على الأجهزة التي تعمل باللمس.</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>
	]]></description>
</item>            <item>
	<title>Native horizontal scrolling</title>
	<pubDate>Sat, 16 Jan 2021 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2021/01/browser-native-horizontal-scrolling/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2021/01/browser-native-horizontal-scrolling/</guid>
	<description><![CDATA[
		<p>I really like <code>overflow-x: scroll</code> and I think it is very much underused in favour for Javascript solutions when it comes to sliders on the web.</p>

<p>I, too, used to default to Javascript solutions until I realised they created more issues in some cases (I also got one-too-many last-minute requests from agency clients to change the mobile behaviour of a section to a slider).</p>

<h2>Rely on the browser</h2>

<p>I am proudly lazy and <code>overflow-x: scroll</code> just works in browsers. Using the horizontal scrollbar to scroll? Works. Scrolling on a touch screen? Works. Scrolling with your keyboard's arrow keys? Works. Your mouse's scroll wheel supports horizontal scrolling? That works too.</p>

<p>No tech debt. No JS dependencies. It just works!</p>

<h2>iOS support</h2>

<p>It's been a while since I tested this on a real iOS device, but last I checked it didn't work well without the <code>-webkit-overflow-scrolling</code> property:</p>

<pre><code data-theme="material-theme-palenight" data-lang="css" class='language-css torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">scroll-x</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">overflow-x</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> scroll</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #89DDFF;">}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span><span style="color: #89DDFF;">@supports</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">(</span><span style="color: #B2CCD6;">-webkit-overflow-scrolling</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> touch</span><span style="color: #89DDFF;">)</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">6</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">scroll-x</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">7</span><span style="color: #A6ACCD;">    </span><span style="color: #B2CCD6;">-webkit-overflow-scrolling</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> touch</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">8</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">9</span><span style="color: #89DDFF;">}</span></div></code></pre>

<h2>Progressive enhancement</h2>

<p>Using <code>overflow-x: scroll</code> does not mean you have to "settle" for a scrollbar if you don't want one. <a href="https://nickpiscitelli.github.io/Glider.js/">Glider.js</a> is a good example of how you can progressively enhance a horizontal scrolling component with Javascript.</p>

<p>I also built a demo slider with <a href="https://github.com/alpinejs/alpine">Alpine.js</a></p>

<p class="codepen" data-height="554" data-theme-id="dark" data-default-tab="result" data-user="hus_hmd" data-slug-hash="jObpqJw" style="height: 554px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Alpine.js slider with native scroll">
  <span>See the Pen <a href="https://codepen.io/hus_hmd/pen/jObpqJw">
  Alpine.js slider with native scroll</a> by Hussein Al Hammad (<a href="https://codepen.io/hus_hmd">@hus_hmd</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>

<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

<h2>Accessibility, UX and content discoverability</h2>

<p>If you have multiple slides each of which contains some focusable elements (e.g. links), navigating a horizontal scrolling component with a keyboard gets you no surprises; the browser scrolls the in-focus element into view (within the component).</p>

<p>Another reason to love <code>overflow-x: scroll</code> is how it keeps content discoverability within a page possible when using the browser's built-in find functionality. Here is an example from the <a href="https://css-tricks.com/">CSS-Tricks home page</a>:</p>

<figure class="video_container">
  <video controls="true" allowfullscreen="true">
    <source src="/assets/videos/overflow-x/css-tricks.mp4" type="video/mp4">
    <source src="/assets/videos/overflow-x/css-tricks.webm" type="video/webm">
  </video>
</figure>

<p>And here is another one from a Glider.js demo:</p>

<figure class="video_container">
  <video controls="true" allowfullscreen="true">
    <source src="/assets/videos/overflow-x/glider.mp4" type="video/mp4">
    <source src="/assets/videos/overflow-x/glider.webm" type="video/webm">
  </video>
</figure>

<h2>Wait, it gets better</h2>

<p>CSS now allows you to customise the scrolling experience within a scrolling element with <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Scroll_Snap">Scroll Snap</a>. Ahmad Shadeed wrote a great <a href="https://www.ishadeed.com/article/css-scroll-snap/">article on Scroll Snap</a> if you want to dive deep into this.</p>
	]]></description>
</item>            <item>
	<title>Internationalised Domain Names</title>
	<pubDate>Tue, 01 Dec 2020 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2020/12/internationalised-domain-names/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2020/12/internationalised-domain-names/</guid>
	<description><![CDATA[
		<p>I have recently launched a new website (<a href="https://وجد.موقع">wajad.art</a>) with an <a href="https://en.wikipedia.org/wiki/Internationalized_domain_name">internationalised domain name (IDN)</a>. The domain name and all the page paths are in Arabic, which makes things more fun given Arabic is written right to left (RTL).</p>

<p>Domain name is <code>وجد.موقع</code>, and <code>موقع</code> is the top-level domain (TLD), which is the equivalent to the <code>site</code> TLD.</p>

<p>A full URL example of page:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #A6ACCD;">وجد.موقع/م/أساطير-خليجية</span></div></code></pre>

<h2>How do IDNs work?</h2>

<p>Given the Domain Name System (DNS) has to use ASCII characters, they store IDNs as ASCII strings using <a href="https://en.wikipedia.org/wiki/Punycode">Punycode</a>, which is:</p>

<blockquote>
  <p>a representation of Unicode with the limited ASCII character subset used for Internet hostnames</p>
</blockquote>

<p>So while my newly launched website's domain name is <code>وجد.موقع</code>, in the DNS it is stored in the Punycode equivalent:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #A6ACCD;">xn--rgbg7e.xn--4gbrim</span></div></code></pre>

<p>Fortunately, there are Punycode converters such as:
- <a href="https://www.punycoder.com/">punycoder.com</a>
- <a href="https://www.punycode.io/">punycode.io</a></p>

<h2>Domain Name vs Page Path</h2>

<p>The IDNs use Punycode to work around the DNS limitation of only supporting ASCII characters. This does not apply to the rest of the URL. You can use Unicode characters in the page path.</p>

<p>MDN's <a href="https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_is_a_URL">What is a URL?</a> is a good resource to learn the different parts of a URL.</p>

<h2>Browsers</h2>

<h3>Unicode vs Punycode (ASCII)</h3>

<p>When navigating to a website with an IDN via the browser address bar, both the Unicode (e.g. <code>وجد.موقع</code>) and the Punycode (e.g. <code>xn--rgbg7e.xn--4gbrim</code>) work.</p>

<p>Even if you typed in the Punycode in the address bar, web browsers may automatically convert the URL to the (human-friendly) Unicode equivalent if the URL meets the browser's IDN policy:
- <a href="https://chromium.googlesource.com/chromium/src/+/master/docs/idn.md">Google Chrome's IDN policy</a>
- <a href="https://wiki.mozilla.org/IDN_Display_Algorithm">Firefox's IDN Display Algorithm</a></p>

<p>The goal of these policies is to protect users from <a href="https://en.wikipedia.org/wiki/IDN_homograph_attack">IDN homograph attack</a>. There are also browser extensions that alerts users if they are on a site that uses Punycode in its domain name.</p>

<p>Early this year, Google Chrome Developers YouTube channel's show HTTP 203 released an episode titled <a href="https://www.youtube.com/watch?v=0-wB1VY3Nrc&amp;t=1130">Humans can't read URLs. How can we fix it?</a>. Jake and Surma briefly discuss how Chrome analyses the URL and when it may choose to display the Punycode over the Unicode.</p>

<h3>RTL vs LTR</h3>

<p>If you ever mixed RTL and LTR languages when typing something on a digital device, you'd certainly have experienced frustrating times attempting to get words to flow correctly. The browser address bar doesn't handle this too well either.</p>

<p>In the case of <code>وجد.موقع</code>, it is read RTL. However, adding the <code>http</code> protocol at the start means the address starts LTR. So you end up with:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #A6ACCD;">https://وجد.موقع</span></div></code></pre>

<p>Even if you set the language in the browser to Arabic, which converts the UI to RTL:</p>

<p><a href="/assets/images/idn/edge-rtl.png"><img src="/assets/images/idn/edge-rtl.png" alt="Microsoft Edge address bar" title="Microsoft Edge" /></a></p>

<p>This is not a huge pain point, but it does look odd. As a developer I know the start is <code>https://</code>. However, to an Arabic speaker who is not familiar with the protocol and its uses, they may interpret this as the URL ends in <code>https://</code>.</p>

<p>This may be slightly off-topic, but it is worth noting that things become even more confusing if you use an ASCII domain name (LTR) with an RTL page path and vice versa:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #A6ACCD;">وجد.موقع/path/to/page</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #A6ACCD;">xn--rgbg7e.xn--4gbrim/الصفحة-1/الصفحة-2</span></div></code></pre>

<h3>Copying the URL</h3>

<p>When you are on a website with an IDN and copy the URL directly from the address bar, what gets copied into your clipboard varies across browsers.</p>

<p>Firefox (83.0) copies the Unicode:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #A6ACCD;">https://وجد.موقع</span></div></code></pre>

<p>Chrome's (87.0.4280.66) behaviour is more sophisticated. If you include the <code>https</code> protocol when you copy the URL from the address bar, it copies the Punycode into your clipboard:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #A6ACCD;">https://xn--rgbg7e.xn--4gbrim</span></div></code></pre>

<p>If you exclude the <code>https</code> protocol, it copies the Unicode:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #A6ACCD;">وجد.موقع</span></div></code></pre>

<p>The above behaviour only applies to the domain name. When it comes to the page path, the behaviour is also inconsistent across browsers.</p>

<p>Firefox (83.0) encodes the page path to its UTF-8 representation when the URL is copied (think JavaScript's <code>encodeURI()</code>, or PHP's <code>urlencode()</code>), which is a huge UX pain for me in general and not only with IDNs. Receiving a URL in a chat app that fills up half my phone's screen with <code>%</code>s and a mix of meaningless digits and English characters is pointless to me as a user.</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #A6ACCD;">https://وجد.موقع/%D9%85/%D8%A3%D8%B3%D8%A7%D8%B7%D9%8A%D8%B1-%D8%AE%D9%84%D9%8A%D8%AC%D9%8A%D8%A9</span></div></code></pre>

<p>On Chrome (87.0.4280.66), if the <code>https</code> protocol is included, it copies the Punycode domain name and the encoded page path:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #A6ACCD;">https://xn--rgbg7e.xn--4gbrim/%D9%85/%D8%A3%D8%B3%D8%A7%D8%B7%D9%8A%D8%B1-%D8%AE%D9%84%D9%8A%D8%AC%D9%8A%D8%A9</span></div></code></pre>

<p>If the <code>https</code> protocol is excluded, it copies the whole URL in Unicode:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #A6ACCD;">وجد.موقع/م/أساطير-خليجية</span></div></code></pre>

<h3>Sharing the URL</h3>

<p>Web browsers on smartphones and tablets offer a built-in sharing option, which gives you the choice to copy the URL or share directly to native apps. The behaviour across browsers here is also inconsistent.</p>

<p>The same browser may not behave consistently when copying the URL from the address vs when copying/sharing the URL using the built-in share option. Samsung Internet (13.0.1.64), for instance, copies the Unicode (domain and page path) if you copy the URL directly from the address bar:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #A6ACCD;">https://وجد.موقع/م/أساطير-خليجية</span></div></code></pre>

<p>However, it copies the Punycode and the encoded page path when using the built-in share option:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #A6ACCD;">https://xn--rgbg7e.xn--4gbrim/%D9%85/%D8%A3%D8%B3%D8%A7%D8%B7%D9%8A%D8%B1-%D8%AE%D9%84%D9%8A%D8%AC%D9%8A%D8%A9</span></div></code></pre>

<h2>JavaScript</h2>

<p>The <a href="https://developer.mozilla.org/en-US/docs/Web/API/Location">Location API</a> returns the domain name in Punycode and encodes page paths:</p>

<pre><code data-theme="material-theme-palenight" data-lang="json" class='language-json torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 1</span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 2</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&quot;</span><span style="color: #C792EA;">ancestorOrigins</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{},</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 3</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&quot;</span><span style="color: #C792EA;">href</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">https://xn--rgbg7e.xn--4gbrim/%D9%85/%D8%A3%D8%B3%D8%A7%D8%B7%D9%8A%D8%B1-%D8%AE%D9%84%D9%8A%D8%AC%D9%8A%D8%A9</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">,</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 4</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&quot;</span><span style="color: #C792EA;">origin</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">https://xn--rgbg7e.xn--4gbrim</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">,</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 5</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&quot;</span><span style="color: #C792EA;">protocol</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">https:</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">,</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 6</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&quot;</span><span style="color: #C792EA;">host</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">xn--rgbg7e.xn--4gbrim</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">,</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 7</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&quot;</span><span style="color: #C792EA;">hostname</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">xn--rgbg7e.xn--4gbrim</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">,</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 8</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&quot;</span><span style="color: #C792EA;">port</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&quot;&quot;</span><span style="color: #89DDFF;">,</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 9</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&quot;</span><span style="color: #C792EA;">pathname</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">/%D9%85/%D8%A3%D8%B3%D8%A7%D8%B7%D9%8A%D8%B1-%D8%AE%D9%84%D9%8A%D8%AC%D9%8A%D8%A9</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">,</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">10</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&quot;</span><span style="color: #C792EA;">search</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&quot;&quot;</span><span style="color: #89DDFF;">,</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">11</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&quot;</span><span style="color: #C792EA;">hash</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&quot;&quot;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">12</span><span style="color: #89DDFF;">}</span></div></code></pre>

<h2>The wilderness</h2>

<p>I have used a number of services in which I had to enter the IDN for Wajad or on which the domain name is displayed.</p>

<h3>Domain registration</h3>

<p>Registering a domain with Punycode with a common TLD like <code>.com</code> is not an obstacle. Some domain registrars allow you to use Unicode when searching domains e.g. <code>وجد.com</code>.</p>

<p>But I was looking for the internationalised TLD <code>موقع</code>. It was not easy finding a domain registrar that sold <code>موقع</code> domains. I ended up on multiple scammy-looking sites during my search. Eventually I bought the domain via <a href="https://www.marcaria.com/">maracaria.com</a>.</p>

<h3>Cloudflare</h3>

<p>I had no issues adding IDNs with Unicode when adding the site to <a href="https://www.cloudflare.com/">Cloudflare</a>. They are also displayed in Unicode in the dashboard:</p>

<p><a href="/assets/images/idn/cloudflare.png"><img src="/assets/images/idn/cloudflare.png" alt="Cloudflare dashboard" /></a></p>

<p>However, Cloudflare used the Punycode in the email notifications they sent to me so far:</p>

<p><a href="/assets/images/idn/cloudflare-email.png"><img src="/assets/images/idn/cloudflare-email.png" alt="Cloudflare email notification" /></a></p>

<h3>Netlify</h3>

<p>Before launching the site, I set up a "coming soon" landing page on <a href="https://www.netlify.com/">Netlify</a>. Unlike Cloudflare, Netlify did not allow me to add the domain name with Unicode, and I had to enter the Punycode equivalent. Netlify's dashboard displays the domain in Punycode:</p>

<p><a href="/assets/images/idn/netlify.png"><img src="/assets/images/idn/netlify.png" alt="Netlify dashboard" /></a></p>

<p>Their email notifications also display the domain in Punycode:</p>

<p><a href="/assets/images/idn/netlify-email.png"><img src="/assets/images/idn/netlify-email.png" alt="Netlify email notification" /></a></p>

<h3>Cloudways</h3>

<p>Wajad's current PHP-based site is hosted on DigitalOcean via <a href="https://www.cloudways.com/">Cloudways</a>. The experience on Cloudways is similar to Netlify and I had to enter the Punycode:</p>

<p><a href="/assets/images/idn/cloudways.png"><img src="/assets/images/idn/cloudways.png" alt="Cloudways dashboard" /></a></p>

<h3>Google Search Console</h3>

<p>I was able to add the site to Google Search Console with the Unicode version of the domain. Oddly, some subsequent forms did not accept Unicode:</p>

<p><a href="/assets/images/idn/search-console-sitemap-form.png"><img src="/assets/images/idn/search-console-sitemap-form.png" alt="Google Search Console sitemap form" /></a></p>

<p>So I had to enter the Punycode equivalent, but Google Search Console displayed the URL in Unicode after submitting the form:</p>

<p><a href="/assets/images/idn/search-console-sitemap-list.png"><img src="/assets/images/idn/search-console-sitemap-list.png" alt="Google Search Console sitemap form" /></a></p>

<p>Fortunately, email notifications use Unicode:</p>

<p><a href="/assets/images/idn/search-console-email.png"><img src="/assets/images/idn/search-console-email.png" alt="Google Search Console sitemap form" /></a></p>

<h3>Google Search results</h3>

<p>Google Search results display the domain name in Unicode. I already knew it displayed Arabic correctly for breadcrumbs, but it is really nice to see the domain name displayed in a human-friendly manner:</p>

<p><a href="/assets/images/idn/google-search.png"><img src="/assets/images/idn/google-search.png" alt="Google search result - IDN" /></a></p>

<p>Both Unicode and Punycode are supported when using <a href="https://support.google.com/websearch/answer/2466433">search operators</a> like <code>site:</code>:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #A6ACCD;">site:وجد.موقع</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #A6ACCD;">site:xn--rgbg7e.xn--4gbrim</span></div></code></pre>

<h3>Bing Webmaster Tools</h3>

<p>Bing Webmaster Tools allow you to import verified sites from Google Search Console. Upon an import attempt it displayed an error message saying the site addition was unsuccessful:</p>

<p><a href="/assets/images/idn/bing-import.png"><img src="/assets/images/idn/bing-import.png" alt="Bing Webmaster Tools - import error message" /></a></p>

<p>I attempted to enter the URL manually as suggested, but the Unicode was not accepted:</p>

<p><a href="/assets/images/idn/bing-import-2.png"><img src="/assets/images/idn/bing-import-2.png" alt="Bing Webmaster Tools - import error message" /></a></p>

<p>Then when I went to check the list of sites under my account, Wajad was actually listed! I'm not entirely sure which of the above attempts was the successful one.</p>

<p>Bing Webmaster Tools lists the domain in Unicode, but when you open the dashboard for the site it lists the Punycode:</p>

<p><a href="/assets/images/idn/bing-sites-list.png"><img src="/assets/images/idn/bing-sites-list.png" alt="Bing Webmaster Tools - sites list" /></a></p>

<p>I had the opposite experience to Google Search Console when submitting the sitemap. The form accepted the Unicode, but the sitemap list displays the Punycode:</p>

<p><a href="/assets/images/idn/bing-sitemap-form.png"><img src="/assets/images/idn/bing-sitemap-form.png" alt="Bing Webmaster Tools - sitemap form" /></a>
<a href="/assets/images/idn/bing-sitemap-list.png"><img src="/assets/images/idn/bing-sitemap-list.png" alt="Bing Webmaster Tools - sitemap list" /></a></p>

<h3>Bing search results</h3>

<p>I have only recently submitted the sitemap via Bing Webmaster Tools, so I still do not know the full picture. From what I can tell so far Bing search results also display the domain in Unicode.</p>

<p>However, it seems only Punycode is supported when using <a href="https://docs.microsoft.com/en-us/previous-versions/bing/search/ff795620(v=msdn.10)">search operators</a> like <code>site:</code>:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #A6ACCD;">site:xn--rgbg7e.xn--4gbrim</span></div></code></pre>

<p><a href="/assets/images/idn/bing-search.png"><img src="/assets/images/idn/bing-search.png" alt="Bing search result - IDN" /></a></p>

<h3>Fathom Analytics</h3>

<p>I had no issue using the Unicode version of the domain when adding the site to <a href="https://usefathom.com/">Fathom Analytics</a>. The domain is always displayed in Unicode (dashboard and email notifications).</p>

<p>Their recently-launched tool <a href="https://usephantom.com/">Phantom Analyzer</a> also allowed me to enter the URL in Unicode, but the results page displayed the domain in Punycode.</p>

<p><a href="/assets/images/idn/phantom-analyzer.png"><img src="/assets/images/idn/phantom-analyzer.png" alt="Phantom Analyzer results" /></a></p>

<h3>Zoho Mail</h3>

<p>Neither Unicode nor Punycode is supported when signing up to <a href="https://www.zoho.com/mail/">Zoho Mail</a>.</p>

<p><a href="/assets/images/idn/zoho-mail.png"><img src="/assets/images/idn/zoho-mail.png" alt="Zoho Mail - Unicode sign up" /></a>
<a href="/assets/images/idn/zoho-mail-punycode.png"><img src="/assets/images/idn/zoho-mail-punycode.png" alt="Zoho Mail - Punycode sign up" /></a></p>

<h3>Emails</h3>

<p>G Suite (now Google Workspace) allowed me to sign up with my IDN. I sent test emails to Gmail, Yahoo and Outlook. Gmail was the only one to display the domain name in Unicode.</p>

<p><a href="/assets/images/idn/gmail.png"><img src="/assets/images/idn/gmail.png" alt="Gmail - received email from IDN with Punycode" /></a>
<a href="/assets/images/idn/outlook.png"><img src="/assets/images/idn/outlook.png" alt="Outlook - received email from IDN with Punycode" /></a>
<a href="/assets/images/idn/yahoo.png"><img src="/assets/images/idn/yahoo.png" alt="Yahoo - received email from IDN with Punycode" /></a></p>

<p>I have also sent HTML email tests with images. Yahoo Mail and Windows Mail did not load images whose <code>src</code> had the domain in Unicode, but Gmail did:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">img</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">src</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">https://وجد.موقع/path/to/image.jpg</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">alt</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;&quot;</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<h3>Auto-linking</h3>

<p>When sending messages via chat apps, adding the <code>https</code> protocol to the URL (with domain name in Unicode) seems to be enough for most apps.</p>

<p>Although email clients are known for linking text in HTML emails when you don't want them to, I found Gmail and Windows Mail don't auto-link:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #A6ACCD;">https://وجد.موقع</span></div></code></pre>

<h3>In-app browsers</h3>

<p>The behaviour of in-app browsers is consistent. On iOS, Instagram's in-app browser displays the domain in Punycode, while Twitter's in-app browser displays the domain in Unicode.</p>

<h2>I understand, but..</h2>

<p>I understand why I'm seeing very different behaviour across browsers and apps, but as a developer and a user I just would love to see a better user experience overall.</p>

<p>Wajad is still a young side project, but it is clear to me that I'll run into more interesting IDN-related scenarios as it grows and I'll try my best to document them.</p>
	]]></description>
</item>            <item>
	<title>The lang Attribute and Fonts</title>
	<pubDate>Fri, 30 Oct 2020 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2020/10/lang-attribute-and-fonts/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2020/10/lang-attribute-and-fonts/</guid>
	<description><![CDATA[
		<p>The <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang">lang attribute</a> is used to define the language of the document or a single element.</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">html</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">lang</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">en</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;&lt;/</span><span style="color: #F07178;">html</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<p>Adding the <code>lang</code> attribute to an element seems to affect which glyphs in a web font some browsers render, and which system font is rendered. Here is a demo where different paragraphs have different <code>lang</code> values, but they all share <code>font-family: sans-serif</code>:</p>

<p class="codepen" data-height="358" data-theme-id="dark" data-default-tab="result" data-user="hus_hmd" data-slug-hash="vYKRWqv" style="height: 358px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="lang attribute and fonts">
  <span>See the Pen <a href="https://codepen.io/hus_hmd/pen/vYKRWqv">
  lang attribute and fonts</a> by Hussein Al Hammad (<a href="https://codepen.io/hus_hmd">@hus_hmd</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>

<p><script async src="https://static.codepen.io/assets/embed/ei.js"></script>
<br>
If you are using Chrome, you can check the "Computed" tab in DevTools to check the rendered font. You may get different fonts depending on the fonts you have installed on your device.</p>

<h2>Is this a problem?</h2>

<p>In the demo above it seems to me that the browser tries to pick a font that is better suited for the specified language, which is a fair behaviour.</p>

<p>However, a number of developers reported that their web font renders differently based on the value of the <code>lang</code> attribute. It looks like the browser renders different glyphs of the same font:</p>

<ul>
<li><a href="https://stackoverflow.com/questions/60602104/change-html-lang-from-en-us-in-to-sr-rs-in-wordpress-change-font-style">Example 1</a></li>
<li><a href="https://stackoverflow.com/questions/43894299/prevent-htmls-lang-attribute-from-changing-diacritics">Example 2</a></li>
</ul>

<p>Luckily you can force the browser to render the font in a specific locale with the <code>-webkit-locale</code> property:</p>

<pre><code data-theme="material-theme-palenight" data-lang="css" class='language-css torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">[</span><span style="color: #C792EA;">lang</span><span style="color: #89DDFF;">=</span><span style="color: #C3E88D;">sr</span><span style="color: #89DDFF;">]</span><span style="color: #A6ACCD;"> </span><span style="color: #FFCB6B;">*</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">    </span><span style="color: #B2CCD6;">-webkit-locale</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">en</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #89DDFF;">}</span></div></code></pre>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">html</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">lang</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">sr</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;&lt;/</span><span style="color: #F07178;">html</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>
	]]></description>
</item>            <item>
	<title>Dark Mode Images</title>
	<pubDate>Thu, 29 Oct 2020 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2020/10/dark-mode-bitmap-images/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2020/10/dark-mode-bitmap-images/</guid>
	<description><![CDATA[
		<p>A bitmap image is one of the things you may not immediately think of when writing CSS and HTML for dark mode. Unlike an SVG, you cannot simply change the colours of the shapes in a bitmap image with CSS by specifying what colours you want them to be. And the approach you would take to handle a bitmap image in dark mode can vary depending on the type of image and the environment you are working in (e.g. web page vs html email).</p>

<p>Here is a quick look at some of the approaches and how you may implement them for web pages and HTML emails.</p>

<h2>1. Using different images</h2>

<h3>Web</h3>

<p>On the web you can rely on the <code>&lt;picture&gt;</code> element for this without writing additional CSS:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">picture</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">source</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">srcset</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">image-dark-mode.jpg</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">media</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">(prefers-color-scheme: dark)</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">img</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">src</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">image.jpg</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">alt</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;&quot;</span><span style="color: #89DDFF;"> /&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">picture</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<p>This approach works if you only serve a dark theme when the user's browser or operating system is set to dark mode. If you provide toggable dark/light themes from within the website, you would implement this differently (e.g. hide/display 2 different <code>&lt;img&gt;</code>s).</p>

<h3>Email</h3>

<p>The <code>&lt;picture&gt;</code> element is <a href="https://www.caniemail.com/features/html-picture/">not widely supported in email clients</a> at the time of writing, so you would hide/display 2 different <code>&lt;img&gt;</code>s with a media query. Litmus's <a href="https://www.litmus.com/blog/the-ultimate-guide-to-dark-mode-for-email-marketers/">The Ultimate Guide to Dark Mode for Email Marketers</a> covers this approach well and goes further to explain how to use the <code>[data-ogsc]</code> attribute selector to target Outlook web and mobile app.</p>

<hr />

<h2>2. Using dark-mode-optimised images</h2>

<p>If you do not wish to use different versions of an image (one for light mode and another for dark mode), in some cases you can optimise an image so it looks well on dark mode. You may use a transparent background and/or add outlines or light-coloured shadows that do not show on light mode.</p>

<h3><span>⚠️</span> Transparent backgrounds</h3>

<p>PNG is probably the most commonly-used image format when a transparent background is needed. The type of the image impacts the file size. When it comes to PNGs, a photograph for instance can be extremely large in size in comparison to other formats such as JPG and WebP. So do not disregard performance here.</p>

<h4>Web</h4>

<p>For simple shapes like circles and rectangles with rounded corners you can rely on CSS instead of using a large PNG.</p>

<p>For more complex shapes, you <em>may</em> be able to use <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/clip-path">clip-path</a>, but not all its features are supported across major browsers at the time of writing.</p>

<h4>Email</h4>

<p>For simple shapes like circles and rectangles with rounded corners you can also rely on CSS instead in HTML emails.</p>

<blockquote>
  <p>But Outlook Windows and Windows Mail do not support <a href="https://www.caniemail.com/features/css-border-radius/">border-radius</a></p>
</blockquote>

<p>You can use <code>border-radius</code> as a progressive enhancement so your email would still look presentable in email clients where it doesn't work. If you must have those rounded corners or circles on Outlook Windows, you can use a VML fallback. VML has a number of predefined shapes such as <code>&lt;v:oval&gt;</code> and <code>&lt;v:roundrect&gt;</code> so it is not a huge amount of extra code:</p>

<pre><code data-theme="material-theme-palenight" data-lang="xml" class='language-xml torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">v</span><span style="color: #89DDFF;">:</span><span style="color: #F07178;">roundrect</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">xmlns</span><span style="color: #89DDFF;">:</span><span style="color: #C792EA;">v</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">urn:schemas-microsoft-com:vml</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">xmlns</span><span style="color: #89DDFF;">:</span><span style="color: #C792EA;">w</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">urn:schemas-microsoft-com:office:word</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">style</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">width:400px; height:200px;</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">arcsize</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">3%</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">v</span><span style="color: #89DDFF;">:</span><span style="color: #F07178;">imagedata</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">src</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">https://example.com/image.jpg</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> /&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">v</span><span style="color: #89DDFF;">:</span><span style="color: #F07178;">roundrect</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<hr />

<h2>3. Inverting colours</h2>

<p>If you have images with white/light background and a black/dark foreground, inverting the colours with CSS's <code>filter</code> property can be an option:</p>

<pre><code data-theme="material-theme-palenight" data-lang="css" class='language-css torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">@media</span><span style="color: #89DDFF;">(</span><span style="color: #A6ACCD;">prefers-color-scheme</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> dark</span><span style="color: #89DDFF;">)</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">dark-scheme--invert</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #A6ACCD;">        </span><span style="color: #B2CCD6;">filter</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #82AAFF;">invert</span><span style="color: #89DDFF;">(</span><span style="color: #F78C6C;">1</span><span style="color: #89DDFF;">);</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span><span style="color: #89DDFF;">}</span></div></code></pre>

<p>On top of that it may make sense to use the <code>mix-blend-mode</code> property in some cases to blend in the image's background with the page's background if it does not match your dark mode background 100%:</p>

<pre><code data-theme="material-theme-palenight" data-lang="css" class='language-css torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">@media</span><span style="color: #89DDFF;">(</span><span style="color: #A6ACCD;">prefers-color-scheme</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> dark</span><span style="color: #89DDFF;">)</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">dark-scheme--invert</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #A6ACCD;">        </span><span style="color: #B2CCD6;">filter</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #82AAFF;">invert</span><span style="color: #89DDFF;">(</span><span style="color: #F78C6C;">1</span><span style="color: #89DDFF;">);</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #A6ACCD;">        </span><span style="color: #B2CCD6;">mix-blend-mode</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> screen</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">6</span><span style="color: #89DDFF;">}</span></div></code></pre>

<p class="codepen" data-height="503" data-theme-id="dark" data-default-tab="result" data-user="hus_hmd" data-slug-hash="YzWazYj" style="height: 503px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Invert image colours">
  <span>See the Pen <a href="https://codepen.io/hus_hmd/pen/YzWazYj">
  Invert image colours</a> by Hussein Al Hammad (<a href="https://codepen.io/hus_hmd">@hus_hmd</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>

<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

<h3>Email</h3>

<p>The <code>prefers-color-scheme</code> media query does not work in all email clients. Neither does the <code>filter</code> property. It is a nice progressive enhancement nonetheless.</p>
	]]></description>
</item>            <item>
	<title>RTL support lands in Bootrstrap and Bulma</title>
	<pubDate>Sat, 18 Jul 2020 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2020/07/rtl-support-lands-in-bootrstrap-and-bulma/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2020/07/rtl-support-lands-in-bootrstrap-and-bulma/</guid>
	<description><![CDATA[
		<p>On 7 June 2020 <a href="https://bulma.io/">Bulma</a> <a href="https://github.com/jgthms/bulma/releases/tag/0.9.0">v0.9.0</a> was released and while I always wanted the spacing helpers it introduced, what I was most excited about is the RTL support!</p>

<p>Shortly after, <a href="https://blog.getbootstrap.com/2020/06/16/bootstrap-5-alpha/">Bootstrap 5 alpha</a> was released and they also announced RTL support!</p>

<p>I live in a part of the world where it is becoming extremely common for websites and web apps to offer both English (LTR) and Arabic (RTL) versions. Seeing popular CSS frameworks add built-in support for RTL is a huge plus for many developers in this region, and I certainly hope more open-source projects follow suit.</p>

<p>Having said that, I do realise my role in this and I feel developers (including me) who want to see RTL support in more open-source projects should be contributing to such projects (if they have the required skill set); particularly if it is a small project with a single or very few maintainers.</p>
	]]></description>
</item>            <item>
	<title>FontAwesomeSVG-PHP v1.2: Duetone support</title>
	<pubDate>Fri, 26 Jun 2020 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2020/06/fontawesomesvg-php-v1-2-duetone-support/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2020/06/fontawesomesvg-php-v1-2-duetone-support/</guid>
	<description><![CDATA[
		<p>Fontawesome added duotone support back in July 2019. So this is a little overdue, but better late than never! You can now use <a href="https://github.com/husseinalhammad/FontAwesomeSVG-PHP">FontAwesomeSVG-PHP</a> v1.2 to output Fontawesome duotone SVG icons with PHP (no JS required).</p>

<p>Output a duoetone icon in single colour:</p>

<pre><code data-theme="material-theme-palenight" data-lang="php" class='language-php torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #82AAFF;">echo</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">FA</span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">get_svg</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">fad fa-laugh-wink</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">[</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">fill</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">=&gt;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">#e64980</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #89DDFF;">]);</span></div></code></pre>

<p>Swap layer opacity by adding the <code>fa-swap-opacity</code> class:</p>

<pre><code data-theme="material-theme-palenight" data-lang="php" class='language-php torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #82AAFF;">echo</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">FA</span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">get_svg</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">fad fa-laugh-wink</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">[</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">fill</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">=&gt;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">#e64980</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">class</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">=&gt;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">fa-swap-opacity</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #89DDFF;">]);</span></div></code></pre>

<p>Single colour with custom opacity:</p>

<pre><code data-theme="material-theme-palenight" data-lang="php" class='language-php torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #82AAFF;">echo</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">FA</span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">get_svg</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">fad fa-laugh-wink</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">[</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">fill</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">=&gt;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">#e64980</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">secondary</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">=&gt;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">[</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #A6ACCD;">        </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">opacity</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">=&gt;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">0.2</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">],</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">6</span><span style="color: #89DDFF;">]);</span></div></code></pre>

<p>Custom colours and opacity:</p>

<pre><code data-theme="material-theme-palenight" data-lang="php" class='language-php torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 1</span><span style="color: #82AAFF;">echo</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">FA</span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">get_svg</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">fad fa-laugh-wink</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">[</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 2</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">primary</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">=&gt;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">[</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 3</span><span style="color: #A6ACCD;">        </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">fill</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">=&gt;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">#e64980</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 4</span><span style="color: #A6ACCD;">        </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">opacity</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">=&gt;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">0.5</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 5</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">],</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 6</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">secondary</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">=&gt;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">[</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 7</span><span style="color: #A6ACCD;">        </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">fill</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">=&gt;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">#fcc417</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 8</span><span style="color: #A6ACCD;">        </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">opacity</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">=&gt;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">1</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 9</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">],</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">10</span><span style="color: #89DDFF;">]);</span></div></code></pre>

<p>Head to the <a href="https://github.com/husseinalhammad/FontAwesomeSVG-PHP">GitHub repo</a> to learn more.</p>
	]]></description>
</item>            <item>
	<title>Alpine.js: responsive x-cloak</title>
	<pubDate>Thu, 02 Apr 2020 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2020/04/alpinejs-responsive-x-cloak/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2020/04/alpinejs-responsive-x-cloak/</guid>
	<description><![CDATA[
		<p>When using <a href="https://github.com/alpinejs/alpine">Alpine.js</a> the <code>x-cloak</code> attribute is removed from DOM elements when Alpine is initialised. As noted in the docs, this makes it useful for hiding elements until Alpine is initialised:</p>

<blockquote>
  <p><code>x-cloak</code> attributes are removed from elements when Alpine initializes. This is useful for hiding pre-initialized DOM. It's typical to add the following global style for this to work:</p>

<pre><code data-theme="material-theme-palenight" data-lang="css" class='language-css torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #A6ACCD;">&lt;</span><span style="color: #FFCB6B;">style</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">[</span><span style="color: #C792EA;">x-cloak</span><span style="color: #89DDFF;">]</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span><span style="color: #A6ACCD;"> </span><span style="color: #B2CCD6;">display</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> none</span><span style="color: #89DDFF;">;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #A6ACCD;">&lt;/</span><span style="color: #FFCB6B;">style</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>
</blockquote>

<p>You may want to hide pre-initialised DOM on some screen sizes, but not others. A good example for this is the links in a site's navigation bar, which are typically hidden by default on smaller screen sizes, but are always visible on wider screens.</p>

<p>So instead of hiding all elements with the <code>x-cloak</code> attribute, we can hide only the ones whose <code>x-cloak</code> attribute has no value:</p>

<pre><code data-theme="material-theme-palenight" data-lang="css" class='language-css torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">[</span><span style="color: #C792EA;">x-cloak</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;&quot;</span><span style="color: #89DDFF;">]</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span><span style="color: #A6ACCD;"> </span><span style="color: #B2CCD6;">display</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> none</span><span style="color: #89DDFF;">;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">}</span></div></code></pre>

<p>This allows us to target elements whose <code>x-cloak</code> attribute has a value:</p>

<pre><code data-theme="material-theme-palenight" data-lang="css" class='language-css torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">[</span><span style="color: #C792EA;">x-cloak</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">some-value</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">]</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">}</span></div></code></pre>

<p>So now we can write CSS rules targeting elements whose <code>x-cloak</code> value is <code>mobile</code>, <code>tablet</code>, <code>desktop</code>:</p>

<pre><code data-theme="material-theme-palenight" data-lang="css" class='language-css torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #676E95;">/* always hidden */</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #89DDFF;">[</span><span style="color: #C792EA;">x-cloak</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;&quot;</span><span style="color: #89DDFF;">]</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span><span style="color: #A6ACCD;"> </span><span style="color: #B2CCD6;">display</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> none</span><span style="color: #89DDFF;">;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #676E95;">/* hidden on mobile/smaller screens */</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span><span style="color: #89DDFF;">@media</span><span style="color: #A6ACCD;"> screen </span><span style="color: #89DDFF;">and</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">(</span><span style="color: #FFCB6B;">max-width</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">768px</span><span style="color: #89DDFF;">)</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">6</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">[</span><span style="color: #C792EA;">x-cloak</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">mobile</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">]</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span><span style="color: #A6ACCD;"> </span><span style="color: #B2CCD6;">display</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> none</span><span style="color: #89DDFF;">;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">7</span><span style="color: #89DDFF;">}</span></div></code></pre>
	]]></description>
</item>            <item>
	<title>FontAwesomeSVG-PHP v1.1: improved accessibility</title>
	<pubDate>Mon, 30 Dec 2019 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2019/12/fontawesomesvg-php-v1-1-improved-accessibility/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2019/12/fontawesomesvg-php-v1-1-improved-accessibility/</guid>
	<description><![CDATA[
		<p>I've updated <a href="https://github.com/husseinalhammad/FontAwesomeSVG-PHP">FontAwesomeSVG-PHP</a> with improved accessibility features.</p>

<p>If you are not familiar with FontAwesomeSVG-PHP, it is a PHP class that inlines <a href="https://fontawesome.com/">Font Awesome</a>'s SVG icons without Javascript. It renders the icons on the server side instead of outputting something like <code>&lt;i class="fas fa-file"&gt;&lt;/i&gt;</code> and have Javascript replace that with the actualy SVG icon on the client side.</p>

<h2>What's new?</h2>

<h3>Automatic <code>aria-labelledby</code></h3>

<p>With the previous version you could add a <code>&lt;title&gt;</code>:</p>

<pre><code data-theme="material-theme-palenight" data-lang="php" class='language-php torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #82AAFF;">echo</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">FA</span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">get_svg</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">fas fa-file</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">[</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">title</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">=&gt;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">File</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #89DDFF;">]);</span></div></code></pre>

<p>The above outputs something like:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">svg</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">title</span><span style="color: #89DDFF;">&gt;</span><span style="color: #A6ACCD;">File</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">title</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">svg</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<p>With the new version you can also add an <code>id</code> to the <code>&lt;title&gt;</code>. If you do, <code>aria-labelledby</code> will be automatically set for you:</p>

<pre><code data-theme="material-theme-palenight" data-lang="php" class='language-php torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #82AAFF;">echo</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">FA</span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">get_svg</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">fas fa-file</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">[</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">title</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">=&gt;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">File</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">title_id</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">=&gt;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">file-id</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #89DDFF;">]);</span></div></code></pre>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">svg</span><span style="color: #A6ACCD;"> </span><span style="color: #C792EA;">aria-labelledby</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">file-id</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">title</span><span style="color: #A6ACCD;"> </span><span style="color: #C792EA;">id</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">file-id</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span><span style="color: #A6ACCD;">File</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">title</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">svg</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<h3><code>aria-*</code> attributes</h3>

<p>You can now add any <code>aria-*</code> attribute to the SVG tag:</p>

<pre><code data-theme="material-theme-palenight" data-lang="php" class='language-php torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #82AAFF;">echo</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">FA</span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">get_svg</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">fas fa-file</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">[</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">aria-label</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">=&gt;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">File</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #89DDFF;">]);</span></div></code></pre>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">svg</span><span style="color: #A6ACCD;"> </span><span style="color: #C792EA;">aria-label</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">File</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;&lt;/</span><span style="color: #F07178;">svg</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<h3><code>aria-hidden</code> attribute</h3>

<p>Decorative icons <a href="https://css-tricks.com/accessible-svgs/#article-header-id-2">should be hidden from screen readers</a>. So <code>aria-hidden="true"</code> is added to the SVG tag by default unless <code>&lt;title id=""&gt;</code> (and hence <code>aria-labelledby</code>) or <code>aria-label</code> is set:</p>

<pre><code data-theme="material-theme-palenight" data-lang="php" class='language-php torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #82AAFF;">echo</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">FA</span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">get_svg</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">fas fa-file</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">);</span></div></code></pre>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">svg</span><span style="color: #A6ACCD;"> </span><span style="color: #C792EA;">aria-hidden</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">true</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;&lt;/</span><span style="color: #F07178;">svg</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>
	]]></description>
</item>            <item>
	<title>Podcast Notes: Should We Be Setting Annual Goals? (The Art of Product #116)</title>
	<pubDate>Mon, 30 Dec 2019 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2019/12/podcast-notes-should-we-be-setting-annual-goals-the-art-of-product-116/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2019/12/podcast-notes-should-we-be-setting-annual-goals-the-art-of-product-116/</guid>
	<description><![CDATA[
		<p>I've been really enjoying <a href="https://artofproductpodcast.com/">The Art of Product podcast</a> lately. In their latest episode of 2019 "<a href="https://artofproductpodcast.com/episode-116">Should We Be Setting Annual Goals?</a>", <a href="https://twitter.com/derrickreimer">Derrick Reimer</a> and <a href="https://twitter.com/r00k">Ben Orenstein</a> discuss annual goals.</p>

<p>What Derrick said about habits vs goals made me think about <a href="https://hussein-alhammad.com/blog/2019/12/podcast-notes-goal-setting-synatx-67">goal setting</a> again:</p>

<blockquote>
  <p>I think it is helpful to at least name, if not goals, then what habits I want to form.That's kind of how I'm thinking about it. Habits over goals or specific milestones. And I think that may align better with the try-to-enjoy-the-journey part. What are the things on the day-to-day that I can cultivate as opposed to just striving towards some end-state.</p>
</blockquote>
	]]></description>
</item>            <item>
	<title>Podcast Notes: Goal Setting (Synatx #67)</title>
	<pubDate>Mon, 16 Dec 2019 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2019/12/podcast-notes-goal-setting-synatx-67/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2019/12/podcast-notes-goal-setting-synatx-67/</guid>
	<description><![CDATA[
		<p>Today I read <a href="https://zapier.com/blog/missing-goals-learning-90-day/">It's okay to miss goals—if you learn from it</a> by <a href="https://twitter.com/FearlessEditor">Hannah Herman</a> on Zapier's blog. It is a good read. It reminded me of Syntax's <a href="https://syntax.fm/show/067/hasty-treat-goal-setting">epidode 67 - Goal Setting</a> which I re-listen to from time to time.</p>

<p>Even though Hannah's article title sounds like it is only focused about learning from missing goals, it also discusses some tips on setting goals. During Syntaxt #67 <a href="https://twitter.com/wesbos">Wes Bos</a> and <a href="https://twitter.com/stolinski">Scott Tolinski</a> discuss strategies for goal setting and explain how they set their own goals.</p>

<p>There is a lot to learn from both the article and the Syntax episode. I realise I slip up on my goals management for various reasons (due to increased workload, poor planning, family obligations, etc). So I find it very helpful to re-listen to the Syntax Goal Setting episode every now and then particularly when I plan on setting new goals.</p>

<p>Goals management requires an active, conscious effort. Syntax #67 is a good reminder of this.</p>
	]]></description>
</item>            <item>
	<title>Podcast Notes: Staying Small (Full Stack Radio #102)</title>
	<pubDate>Sun, 01 Dec 2019 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2019/12/podcast-notes-staying-small-full-stack-radio-102/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2019/12/podcast-notes-staying-small-full-stack-radio-102/</guid>
	<description><![CDATA[
		<p>Every now and then I listen to a podcast episode that resonates with me or changes the way I think. I end up listening to these episodes multiple times. So I've decided to blog about these episodes, and I'm starting with my all-time favourite:</p>

<h2>Full Stack Radio #102: Paul Jarvis - Staying Small</h2>

<p>Full Stack Radio's <a href="https://twitter.com/adamwathan">Adam Wathan</a> "talks to <a href="https://twitter.com/pjrvs">Paul Jarvis</a> about defining your own version of success and why you might not need to build a big business to achieve it."</p>

<p>This is the podcast episode I have listened to the most. I listened to Adam and Paul discuss running an intentionally small business (and still reaching your professional and personal goals) close to 10 times already and I'm certain that I will revisit it again.</p>

<p>I don't think an attempt to summarise this episode would suffice so I highly recommend listening to <a href="http://www.fullstackradio.com/102">the episode</a> and reading Paul's book: <a href="https://ofone.co/">Company of One</a>.</p>
	]]></description>
</item>            <item>
	<title>Bidirectional horizontal rules in CSS</title>
	<pubDate>Tue, 22 Oct 2019 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2019/10/bidirectional-horizontal-rules-in-css/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2019/10/bidirectional-horizontal-rules-in-css/</guid>
	<description><![CDATA[
		<p>The <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir">dir attribute in HTML</a> and the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/direction">dir property in CSS</a> can be used to set the direction of text and horizontal flow. Some languages are written Left-To-Right (LTR), while others are written Right-To-Left (RTL). So having this level of control is vital for creating documents and interfaces for the web.</p>

<p>Building an interface to support both LTR and RTL layouts is a challenge. Flexbox and Grid certainly makes things easier, but they don't cover all our styling needs.</p>

<p>There are many CSS rules we write in which we specify a physical direction or side. For instance, when we write CSS to layout horizontal elements, it is common to set a margin only on a single side and set a margin of <code>0</code> (on the same side) to the first or last sibling element.</p>

<pre><code data-theme="material-theme-palenight" data-lang="css" class='language-css torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">element</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">    </span><span style="color: #B2CCD6;">margin-right</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">1rem</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #89DDFF;">}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">element</span><span style="color: #89DDFF;">:</span><span style="color: #C792EA;">last-child</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">6</span><span style="color: #A6ACCD;">    </span><span style="color: #B2CCD6;">margin-right</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">0</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">7</span><span style="color: #89DDFF;">}</span></div></code></pre>

<p>Or</p>

<pre><code data-theme="material-theme-palenight" data-lang="css" class='language-css torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">element</span><span style="color: #89DDFF;">:</span><span style="color: #C792EA;">not</span><span style="color: #89DDFF;">(:</span><span style="color: #C792EA;">last-child</span><span style="color: #89DDFF;">)</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">    </span><span style="color: #B2CCD6;">margin-right</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">1rem</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #89DDFF;">}</span></div></code></pre>

<p>In the above example we are adding a margin on the right side of each element except the last. That's how it is written. Though what we mean when we write this is to add a margin <em>after</em> the element in the direction of the horizontal flow of the document (or the parent element).</p>

<p>A common way to style <code>blockquote</code> is to add a border to one side of the quote; the side we consider to come <em>before</em> the quote.</p>

<p>This is <a href="https://bulma.io/">Bulma</a>'s <code>&lt;blockquote&gt;</code> styling:</p>

<pre><code data-theme="material-theme-palenight" data-lang="css" class='language-css torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">content</span><span style="color: #A6ACCD;"> </span><span style="color: #FFCB6B;">blockquote</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">    </span><span style="color: #B2CCD6;">background-color</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">#</span><span style="color: #A6ACCD;">f5f5f5</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #A6ACCD;">    </span><span style="color: #B2CCD6;">border-left</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">5px</span><span style="color: #A6ACCD;"> solid </span><span style="color: #89DDFF;">#</span><span style="color: #A6ACCD;">dbdbdb</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #A6ACCD;">    </span><span style="color: #B2CCD6;">padding</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">1.25em</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">1.5em</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span><span style="color: #89DDFF;">}</span></div></code></pre>

<p>And there are many other styling choices we make in which we specify a side/direction (left/right) in CSS, but what we actually mean is before/after.</p>

<p>So when building interfaces that support both LTR and RTL layouts, one option would be to write CSS rules like:</p>

<pre><code data-theme="material-theme-palenight" data-lang="css" class='language-css torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">class</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #FFCB6B;">html</span><span style="color: #89DDFF;">[</span><span style="color: #C792EA;">dir</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">rtl</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">]</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">class</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{}</span></div></code></pre>

<p>Or perhaps even load different stylesheets for LTR and RTL layouts. However, both options require we either write more CSS or <a href="https://gist.github.com/Integralist/7269907">set up our tooling to generate the appropriate flipped styles</a>. And there are also <a href="https://rtl.daskhat.ir/">tools that convert LTR to RTL styles</a> for you.</p>

<p>Wouldn't it be nice to work with a lower level of abstraction instead? What if we can tell the browser we are targeting the side before/after an element instead of referring to the physical directions left/right? At the end of the day this is what we mean a lot of the times. This reminds me of this <a href="https://twitter.com/keithjgrant/status/842728744653676544">tweet</a>.</p>

<p>CSS has what is called <em>logical properties</em>. We can now tell the browser what we actually mean. Instead of using <code>-left</code> and <code>-right</code>, we can use <code>-inline-start</code> and <code>-inline-end</code>:</p>

<pre><code data-theme="material-theme-palenight" data-lang="css" class='language-css torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">element</span><span style="color: #89DDFF;">:</span><span style="color: #C792EA;">not</span><span style="color: #89DDFF;">(:</span><span style="color: #C792EA;">last-child</span><span style="color: #89DDFF;">)</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">    </span><span style="color: #B2CCD6;">margin-inline-end</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">1rem</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #89DDFF;">}</span></div></code></pre>

<p>Similarly instead of using <code>-top</code> and <code>-bottom</code>, we can use <code>-block-start</code> and <code>-block-end</code>.</p>

<p>This means we can write one set of rules that target both LTR and RTL layouts. Here is an example using the <code>margin-inline-end</code> property:</p>

<p class="codepen" data-height="265" data-theme-id="0" data-default-tab="result" data-user="hus_hmd" data-slug-hash="bGGgMZd" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Logical properties demo: inline list">
  <span>See the Pen <a href="https://codepen.io/hus_hmd/pen/bGGgMZd">
  Logical properties demo: inline list</a> by Hussein Al Hammad (<a href="https://codepen.io/hus_hmd">@hus_hmd</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>

<p><br></p>

<p>Using the <code>border-inline-start</code> and <code>padding-inline-start</code> properties:</p>

<p class="codepen" data-height="265" data-theme-id="0" data-default-tab="result" data-user="hus_hmd" data-slug-hash="vYYgrBM" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Logical properties demo: blockquote">
  <span>See the Pen <a href="https://codepen.io/hus_hmd/pen/vYYgrBM">
  Logical properties demo: blockquote</a> by Hussein Al Hammad (<a href="https://codepen.io/hus_hmd">@hus_hmd</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>

<p><br></p>

<p>Firefox also supports <code>border-*-*-radius</code> so you can target different corners with <code>border-start-start-radius</code>, <code>border-end-start-radius</code>, etc.</p>

<p class="codepen" data-height="265" data-theme-id="0" data-default-tab="result" data-user="hus_hmd" data-slug-hash="abbpKzK" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Logical properties demo: inline form">
  <span>
    See the Pen <a href="https://codepen.io/hus_hmd/pen/abbpKzK"> Logical properties demo: inline form</a> by Hussein Al Hammad (<a href="https://codepen.io/hus_hmd">@hus_hmd</a>)
    on <a href="https://codepen.io">CodePen</a>.
  </span>
</p>

<p>All demos:</p>

<p class="codepen" data-height="465" data-theme-id="0" data-default-tab="result" data-user="hus_hmd" data-slug-hash="YzzNzPp" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Logical properties demo">
  <span>See the Pen <a href="https://codepen.io/hus_hmd/pen/YzzNzPp">
  Logical properties demo</a> by Hussein Al Hammad (<a href="https://codepen.io/hus_hmd">@hus_hmd</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>

<p>For a deeper explanation, you can refer to Rachel Andrew's article <a href="https://www.smashingmagazine.com/2018/03/understanding-logical-properties-values/">Understanding Logical Properties And Values</a>. This is not just about RTL interfaces or horizontal bidirectional CSS rules. Rachel's article also covers logical dimensions.</p>

<h2>Browser Support</h2>

<p>Can I Use?:</p>

<ul>
<li><a href="https://caniuse.com/#feat=css-logical-props">Logical Properties</a></li>
<li><a href="https://caniuse.com/#feat=mdn-css_properties_border-start-start-radius">border-&#42;-&#42;-radius</a></li>
</ul>

<script async src="https://static.codepen.io/assets/embed/ei.js"></script>
	]]></description>
</item>            <item>
	<title>Propertybase API - IN filter: empty option</title>
	<pubDate>Wed, 25 Sep 2019 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2019/09/propertybase-api-in-filter-empty-option/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2019/09/propertybase-api-in-filter-empty-option/</guid>
	<description><![CDATA[
		<p><a href="https://www.propertybase.com/">Propertybase</a> is a real estate solution for real estate agencies. You can use it to manage your property listings and sync the listings with your website via their <a href="https://help.propertybase.com/hc/en-us/articles/202900036-Weblisting-Show-your-Listings-on-your-Website">API</a>.</p>

<p>They have a nice simple <a href="https://help.propertybase.com/hc/en-us/articles/202900036-Weblisting-Show-your-Listings-on-your-Website">API for fetching filtered property listings</a>. However, it was unclear to me how to filter the listings by a field whose value is empty or set to a certain value.</p>

<p>The syntax for filtering by a field that is empty:</p>

<pre><code data-theme="material-theme-palenight" data-lang="text" class='torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #A6ACCD;">Fieldname__c=</span></div></code></pre>

<p>The syntax for filtering by a field whose value is either <code>test</code> or <code>unicorn</code>:</p>

<pre><code data-theme="material-theme-palenight" data-lang="text" class='torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #A6ACCD;">Fieldname__c=IN(test;unicorn)</span></div></code></pre>

<p>If you want to filter by a field whose value is empty, <code>test</code> or <code>unicorn</code>, you can do so by prepending an additional <code>;</code> to your options:</p>

<pre><code data-theme="material-theme-palenight" data-lang="text" class='torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #A6ACCD;">Fieldname__c=IN(;test;unicorn)</span></div></code></pre>
	]]></description>
</item>            <item>
	<title>Every developer builds a CMS!</title>
	<pubDate>Wed, 25 Sep 2019 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2019/09/every-developer-builds-a-cms/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2019/09/every-developer-builds-a-cms/</guid>
	<description><![CDATA[
		<p>"Every developer eventually builds a CMS" is a joke I came across enough times that I promised myself I wouldn't do it. Although I think the joke often comes from developers who have been in the industry for a lot longer than I have, and perhaps enough of them either built or came across custom CMS solutions.</p>

<p>Luckily, we are no longer in the era in which building a bespoke CMS is common for one-off projects. On the other hand, while there are many CMS products in the market we are very much in the era of using (or repurposing) SaaS applications as headless Content Management Systems.</p>

<p>Many applications have nice interfaces, customisable data structures, native mobile apps and developer-friendly APIs. Some even have built-in field types! The ability to build custom extensions provided by some applications also open doors to fill in any gaps in order to utilise them as CMS solutions.</p>

<h2>CMS has a competition</h2>

<p>Content has a new potential home. CMS products have competition from products that are not classified as a CMS or site builders and, in many cases, not built to solve the same problems. These products can perform many of the same tasks and some may even out-perform traditional CMS products in some aspects.</p>

<p>The bar is a little higher for CMS products nowadays. The expected feature list is a bit longer ranging from built-in third-party integrations to real-time collaboration and from customisability to ease-of-use.</p>

<p>The CMS target audience no longer compares a CMS product only against other CMS products, but also against other digital products. A Content Management System is a digital product after all. When users are exposed to a wider range of digital products and find a feature has become so common amongst these products, they may eventually expect the average CMS to support it too.</p>

<h2>What can you use as a CMS?</h2>

<p>There are several products you can use as a CMS:</p>

<ul>
<li><a href="https://github.com/max-barry/google-drive-cms">Google Drive</a></li>
<li><a href="https://blog.usejournal.com/how-to-use-google-sheets-as-a-cms-or-a-database-f9d8e736fdce">Google Sheets</a></li>
<li><a href="https://github.com/postlight/liftoff">Airtable</a> / more <a href="https://airtable.com/universe/exp6Y34FZJdv6mKS5/twilios-lightweight-cms">Airtable</a> / <a href="https://blog.airtable.com/build-your-own-custom-blog-cms-with-airtable-and-gatsbyjs/">Aritable + Gatsby</a> / even CSS-Tricks has an article on using <a href="https://css-tricks.com/getting-to-grips-with-the-airtable-api/">Airtable</a> as a CMS</li>
<li><a href="https://github.com/Troglio/troglio">Trello</a></li>
<li><a href="https://flyingsquirrelbook.com/notes/2/evernote-as-a-cms/">Evernote</a></li>
</ul>

<p>Zenkit, a SaaS product that offers a good deal of customisability, even suggests on <a href="https://zenkit.com/en/use-cases/project-management/">their website</a> that you can use the product as a CMS:</p>

<blockquote>
  <p>Don’t just manage projects – create a CRM, <strong>CMS</strong>, ERP, or anything else you need.</p>
</blockquote>

<p>And you probably can create your own custom headless CMS solution using <a href="https://www.zoho.com/creator/">Zoho Creator</a>, a low-code platform.</p>

<h2>Not a one-size-fits-all</h2>

<p>As with almost everything in the tech world, using a digital product that isn't a CMS to manage and deliver content to websites does not suit all use cases. I'm not claiming that. However, this approach is certainly viable.</p>

<p>We've seen site builders compete against CMS products and services such as Squarespace and Wix now serve a market that otherwise would rely on a CMS to power their websites. We've seen many developers ditch CMS solutions in favour for static site generators when possible. So I don't think it is too crazy to consider some SaaS products as alternatives to content management systems, especially when a product like Airtable can also be used for managing other aspects of a business (e.g. project management, CRM).</p>

<p>I do, however, think the headless CMS scene has great options. So unless a company is already using a product for managing other aspects of the business, it probably makes more sense to pick a tool that was designed for managing and delivering content.</p>

<hr />

<p>For comments, you can go to the <a href="https://dev.to/hus_hmd/every-developer-builds-a-cms-1n47">DEV.to version</a> of this article.</p>
	]]></description>
</item>            <item>
	<title>I have updated my site</title>
	<pubDate>Fri, 19 Jul 2019 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2019/07/i-have-updated-my-site/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2019/07/i-have-updated-my-site/</guid>
	<description><![CDATA[
		<p>I have recently updated my site. This is the third version of the site since I launched it in 2015. I soft-launched this version a few months ago, and while I am still polishing things I am pleased of the increase I have seen in the leads I am getting through the website so far.</p>

<h2>Blog</h2>

<p>While I have not been publishing on this blog consistently, I wrote a lot more on my Perch-focused blog, <a href="https://grabapipit.com">Pipits</a>. One thing I learned from publishing technical articles is that it helps me more than anyone else. It is true what you hear, you get a better grasp of concepts by explaining them. So this is something I would love to keep doing.</p>

<p>I'm hopefully going to be organised enough to consistently blog on here as well. It's not necessairly going to be all focused on web development, but I can see that being the main topic.</p>

<h2>Arabic version</h2>

<p>Now that I am back in the middle east, I feel ignorant not having an Arabic version of this site even though the majority of my current clients do not speak Arabic or require websites in Arabic. So I am working on one!</p>

<p>I have not decided whether I am going to start blogging in Arabic as well. It would certainly be nice. I silently complain about the quality of Arabic content on the web and by not publishing any Arabic content I feel I'm somehow part of the problem.</p>
	]]></description>
</item>            <item>
	<title>Adding Font Awesome SVG icons with PHP</title>
	<pubDate>Fri, 18 May 2018 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2018/05/adding-font-awesome-svg-icons-with-php/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2018/05/adding-font-awesome-svg-icons-with-php/</guid>
	<description><![CDATA[
		<p>Font Awesome is a popular icon library. It used to be an icon font, but now in Font Awesome 5 it also come with SVG icons.</p>

<p>One reason of Font Awesome's popularity is the ease of use. You add a CSS file and a JS file to your document, and you're set. You can then add the icons like so:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">i</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">class</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">fas fa-home</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;&lt;/</span><span style="color: #F07178;">i</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<p>When the SVG icons option was introduced, they kept it just as simple which is great because SVGs are great.</p>

<p>However, you may not necessairly want to load extra Javascript on your PHP application or website just to include icons. And it would be nice to have the SVG icons on there as soon as the page loads - which is one of the benefits of using inline SVGs to begin with.</p>

<p>So I wrote a PHP class to allow me to add the SVG icons almost just as easily. You can find it on <a href="https://github.com/husseinalhammad/FontAwesomeSVG-PHP">GItHub</a>.</p>

<pre><code data-theme="material-theme-palenight" data-lang="php" class='language-php torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">FA </span><span style="color: #89DDFF;">=</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">new</span><span style="color: #A6ACCD;"> </span><span style="color: #FFCB6B;">FontAwesomeSVG</span><span style="color: #89DDFF;">($</span><span style="color: #A6ACCD;">dir</span><span style="color: #89DDFF;">);</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #82AAFF;">echo</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">$</span><span style="color: #A6ACCD;">FA</span><span style="color: #89DDFF;">-&gt;</span><span style="color: #82AAFF;">get_svg</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">fas fa-file</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">);</span></div></code></pre>

<p>I've kept the same behaviour when it comes to classes and attributes. And you also still get the <a href="https://fontawesome.com/how-to-use/accessibility">auto-accessibility feature</a>.</p>

<p>For semantic icons, the class adds a <code>&lt;title&gt;</code> inside the SVG like the default JS method does. However it does not add the <code>aria-labelledby</code> attribute at the moment.</p>

<h2>Example usage</h2>

<p>I have also built a <a href="https://grabapipit.com/news/the-font-awesome-app">Perch app</a> to make it possible to add the SVG icons via PHP or directly in a Perch template. Using the app I can add icons to Perch templates simply like so:</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">perch:fa</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">id</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">home</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">icon</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">fas fa-home</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">html</span><span style="color: #89DDFF;"> &gt;</span></div></code></pre>

<p>Or via PHP like so:</p>

<pre><code data-theme="material-theme-palenight" data-lang="php" class='language-php torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #82AAFF;">pipit_fa_icon</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">fas fa-home</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">);</span></div></code></pre>
	]]></description>
</item>            <item>
	<title>Announcing Pipits for Perch</title>
	<pubDate>Mon, 04 Dec 2017 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2017/12/announcing-pipits-for-perch/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2017/12/announcing-pipits-for-perch/</guid>
	<description><![CDATA[
		<p>I have recently launched <a href="https://grabapipit.com">Pipits for Perch</a>, a collection of Perch add-ons, aiming to help developers build feature-rich Perch websites faster. You can find out more about it and Perch on <a href="https://grabapipit.com/news/introducing-pipits-for-perch">here</a>.</p>

<p>There's also a <a href="https://grabapipit.com/blog">blog</a> on the website about working with Perch and Perch Runway. So I may not post many Perch-related posts on this blog.</p>

<p>The response from the Perch community has been positive and hopefully Pipits for Perch will grow into something very useful to the Perch community.</p>

<p>Logo designed by <a href="https://www.sarahboese.com.au/">Sarah Boese</a>.</p>

<h2>Vision</h2>

<p>I see it not only being useful to the average Perch developer, but also to the front-end developer (and designer) who's not familiar with PHP, to the developer who's using Perch or Runway for the first time and perhaps to the beginner who's looking to perform his first CMS integration.</p>

<p>I remember as a beginner looking to use a CMS for the first time and most of the options I found required the creation of <code>themes</code> which was a turn-off. I just wanted a site with editable content. I didn't want to couple my front-end with a CMS.</p>

<p>So I was really excited when I learned about Perch for the first time (through the <a href="http://www.unfinished.bz/">Unfinished Business</a> podcast) and how I didn't have to create themes to integrate it with a website. And as I grew as a developer and being familiar with Perch, Perch Runway seemed the obvious choice to use on more complex projects and it also doesn't have the concepts of themes.</p>

<p>A CMS like Perch seems to me like a great option for beginners and as they build upon their skills and start to build bigger, more complex sites they can start using Runway.</p>

<p>The thing is apart from the Perch documentation and forum, there wasn't enough learning resources on the web. Now there's a new resource: the blog on Pipits for Perch which hopefully can help developers/designers regardless of their experience. That's why some of the posts on there discuss some basic usage and others discuss more complex concepts.</p>

<p>You can also find Pipits for Perch on <a href="https://twitter.com/grabapipit">Twitter</a> and <a href="https://github.com/Pipits/">GitHub</a>.</p>
	]]></description>
</item>            <item>
	<title>Users Are Forgetful, but Not Amnesic</title>
	<pubDate>Tue, 30 May 2017 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2017/05/users-are-forgetful-but-not-amnesic/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2017/05/users-are-forgetful-but-not-amnesic/</guid>
	<description><![CDATA[
		<p>Many websites and apps let you sign up via social logins, signing up to the
website/app using the credentials from popular social networks such as
Facebook and Twitter, and rightfully so.</p>

<p>Who wants to go through the trouble of filling in a form, remembering yet
another password and verifying their email address or phone number? Instead
most would rather just do it in two clicks via a social login.</p>

<p>But there doesn’t seem to be a user-centred pattern of implementing a social
login. We implement the social login solution to free user from certain tasks,
but perhaps we haven’t looked thoroughly enough at the best way to implement
this.</p>

<p>Don’t get me wrong; I’m not against social logins. It’s the way it’s
implemented that I want to discuss.</p>

<h2>Am I signing in or up?</h2>

<p>Many websites use ‘sign in with Facebook’ or ‘log in with Twitter’ regardless
whether the user is signing in to their existing account or creating a new
one. Why confuse your users?</p>

<p>Gitlab, for example, have a section for social logins below their sign in and
registration forms. Whether the user is on the ‘Sign in’ form or the
‘Register’ form the section says “Sign in with”.</p>

<figure class="image-wrapper">
  <img
    src="/perch/resources/gitlabsignin.png"
    alt="GitLab sign in and registration forms"
  />
</figure>

<p>Do I need to have an existing GitLab account linked to my social media account
to be able to sign in using it? How do I register using a social media
account? It’s not clear. Thankfully GitLab’s users are a bit more tech-savvy
than your average user.</p>

<h2>We are forgetful</h2>

<p>Using a social login means as a user I don’t have to remember another
password. Though it also means I have to remember which social login I use
when I sign up to an app. Many apps include multiple social login options
(e.g. Facebook, Twitter and Google). GitLab (shown above) uses 4, and I have
accounts with 3 of these.</p>

<p>In case you forgot which one you used, it’s not clear to the average user what
to do. There’s no guidance. There’s no ‘Forgot your password?’ equivalent.</p>

<p>It is actually easy to figure out which one it was. When you use a social
login on a third-party app for the first time, you are asked to authorise the
third-party app to access your credentials. If it’s not the first time, you
log in straight away.</p>

<p>So in order to figure out which social network you used, you can try to log in
with each and see what happens. If you pick Twitter, for example, and Twitter
asks you to authorise the third-party app to access your information, then
Twitter wasn’t the social network you used for sign up for this app.</p>

<p>Or you could look at the list of the authorised apps on your social media
accounts (a lot of work).</p>

<p>Does the average user know this though?</p>

<p>Consider this: a user forgets which social network they used to create their
account on a third-party app and they guess it was Facebook. They pick
Facebook and get asked to authorise the app to access their Facebook profile
information (they never used Facebook to log in to this app before), they
confirm the authorisation (thinking they signed up with Facebook and
authorising the app is a regular thing they need to do every time they log in)
and end up with a new account. They have two accounts now (opps!).</p>

<p>They still have to find out which social network they used to sign up in the
first place, then either merge the 2 accounts if the app allow such an option
or delete the unintentionally created account.</p>

<p>What if they were aware that Facebook asking them to authorise the third-party
app means they never used their Facebook account to sign up to this app? What
if you told them? Shouldn’t you?</p>

<blockquote>
  What if you told them? Shouldn't you?
</blockquote>

<p>Even if users are aware of the try-and-eliminate method and/or the fact they
can check the authorised apps list on their social media accounts, you still
should remind them. Even if they are aware that Twitter asking them to
authorise an app means they never used their Twitter account to create an
account on your website, you still should remind them.</p>

<p>Why? Humans forget.</p>

<p>I uninstalled the Medium app because of this. I created my account via the
browser on my desktop using a social login and later decided to download the
app on my phone. I indeed forgot which social media account I used to create
my Medium account. The Medium app doesn’t let you access content unless you
are logged in (another topic), so whenever you open the app even if it is
through a link to a Medium blog post, you are asked to log in to access the
content.</p>

<figure class="image-wrapper">
  <img
    src="/perch/resources/medium-signin.jpg"
    alt="Medium App sign in screen"
  />
</figure>

<p>I also happened to forget the fact that a social network asks you to authorise
an app when you try to log in to an app with said social network account for
the first time. So I didn’t think of the try-and-eliminate method at the time.
So every time I opened the app, I stared at the log in screen for a few
seconds thinking if I pick the wrong social network, I would end up with two
Medium accounts. I didn’t want two Medium accounts. So guess what I did? I
closed the app every time. Eventually, I uninstalled the app.</p>

<h2>So what?</h2>

<p>From a UX point of view, I think there should be a way to help the user figure
out which social network they used to create their accounts on third-party
websites/apps from within the third-party website/app. Be it an automated
method or by providing helpful tips.</p>

<p>So far the only app I encountered that put an effort into this is Line. Line
allows you to log in with an email address (+password), a phone number or a
social media account. I failed to log in with an email address and a password.
So I thought I may have used Facebook to sign up. I chose to log in with
Facebook, I was asked to authorise the app. At this stage I knew my Line
account wasn’t connected to my Facebook account, but I authorised it anyway.</p>

<p>Then Line informed me (perhaps because I chose the ‘sign in’ option rather
than the ‘sign up’) that I have never created an account with them using this
Facebook account and whether I wished to proceed and create the account.</p>

<figure class="image-wrapper">
  <img src="/perch/resources/line-signin.jpg" alt="Line App sign in screen" />
</figure>

<h2>Bonus rant: I’m forgetful, but not amnesic</h2>

<p>Ok so websites and apps allow users to use social logins because humans are
forgetful (and lazy). However, I came across some that implement social logins
in an unexpected way.</p>

<p>Below is a screen shot of Shahid MBC’s sign up form (it’s in Arabic). At the
top you have a Facebook-blue button that reads “sign up with Facebook”. Then
you have a horizontal line separating the Facebook button from the sign up
form. By looking at it you would think that you have two options for signing
up: 1-Social login with Facebook 2-Regular sign up form.</p>

<figure class="image-wrapper">
  <img
    src="/perch/resources/shahid-signup-form.png"
    alt="MBC Shahid sign up form"
  />
</figure>

<p>While the design suggests so, “signing up” with Facebook doesn’t actually sign
you up. It just retrieves some of your information and fill in your name,
email address and gender for you. It also makes these field uneditable. At the
end, you still need to enter a password, your date of birth and a freaking
captcha.</p>

<figure class="image-wrapper">
  <img
    src="/perch/resources/shahid-signup-form2.png"
    alt="MBC Shahid social login option"
  />
</figure>

<p>Instead of signing up to their free trial I chose to close the browser’s tab.
I can be forgetful, but I’m not amnesic. I can enter my own name and email
address. The reason I wanted to sign up with Facebook is so I don’t have to
remember yet another password.</p>

<p>Shahid is just an example. I came across other websites that did the same
thing. Interestingly, all these websites are middle eastern based.</p>

<h2>The point</h2>

<p>Adding social logins to your app provides a great option for your users.
Though, Social networks won’t do your work for you. Your app’s sign in/sign
up/credential retrieval flow is still part of your user experience. Don’t
neglect it. If you can help your users, do it. Users have better things to
remember than whether they used Facebook, Twitter or Google to sign up to your
app.</p>
	]]></description>
</item>            <item>
	<title>Maintainable Perch Templates</title>
	<pubDate>Wed, 26 Apr 2017 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2017/04/maintainable-perch-templates/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2017/04/maintainable-perch-templates/</guid>
	<description><![CDATA[
		<p>Some of your
<a href="https://grabaperch.com/" target="_blank">Perch</a> templates might be
quite long. For example, a typical product template (using the Perch Shop app)
has 20+ Perch tags: the default tags you need to keep and the custom tags you
may add.</p>

<p>When you start mixing your HTML with so many Perch tags, it can get a little
messy. Firstly, there can be a lot in there: you have Perch tags that contain
many attributes that improve your edit form and have no impact on the HTML
rendering such as <code class="language-markup">help</code>,
<code class="language-markup">size</code> and
<code class="language-markup">divider-before</code> as well as other necessary
attributes such as <code class="language-markup">id</code>,
<code class="language-markup">type</code> and
<code class="language-markup">label</code>. Then you got to think about the
order of the fields on the edit form (which can be very different from what
you need for your markup) so you start adding
<code class="language-markup">order</code> attributes to these tags.</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">perch:shop</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 2</span><span style="color: #89DDFF;">  </span><span style="color: #C792EA;">id</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">short_desc</span><span style="color: #89DDFF;">&quot;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 3</span><span style="color: #89DDFF;">  </span><span style="color: #C792EA;">type</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">textarea</span><span style="color: #89DDFF;">&quot;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 4</span><span style="color: #89DDFF;">  </span><span style="color: #C792EA;">label</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">Short Description</span><span style="color: #89DDFF;">&quot;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 5</span><span style="color: #89DDFF;">  </span><span style="color: #C792EA;">markdown</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">true</span><span style="color: #89DDFF;">&quot;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 6</span><span style="color: #89DDFF;">  </span><span style="color: #C792EA;">editor</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">markitup</span><span style="color: #89DDFF;">&quot;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 7</span><span style="color: #89DDFF;">  </span><span style="color: #C792EA;">size</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">s</span><span style="color: #89DDFF;">&quot;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 8</span><span style="color: #89DDFF;">  </span><span style="color: #C792EA;">help</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">This is displayed above the product options. It should be a concise description that highlights the product&#39;s best features</span><span style="color: #89DDFF;">&quot;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 9</span><span style="color: #89DDFF;">  </span><span style="color: #C792EA;">order</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">4</span><span style="color: #89DDFF;">&quot;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">10</span><span style="color: #89DDFF;">  </span><span style="color: #C792EA;">divider-before</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">Product Details</span><span style="color: #89DDFF;">&quot;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">11</span><span style="color: #89DDFF;">/&gt;</span></div></code></pre>

<p>In small templates, it doesn’t feel like much. You can see where everything is
and edit whatever you want quickly. However, in a big template things can get
lost especially if you are reviewing a template you had written a while ago.</p>

<h2>What you can do</h2>

<p>Generally, you need a Perch template for two tasks:</p>

<ol>
<li><p>To create (and edit) content/data for an item (product, blog post, etc)</p></li>
<li><p>To retrieve content and display it on a page</p></li>
</ol>

<p>Perch is very flexible, so you can have a template to create the content and
use another template to display it on a page. You can create some separation
here to make things more manageable, more maintainable.</p>

<h3>Example:</h3>

<p>The following Perch Shop function uses the default product template
(perch/template/shop/products/product.html):</p>

<pre><code data-theme="material-theme-palenight" data-lang="php" class='language-php torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #82AAFF;">perch_shop_product</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">my-product</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">);</span></div></code></pre>

<p>By removing all your markup from the default product template (leaving Perch
tags only), it will be easier to manage your edit form. You can move tags
around as you wish and not worry about using any order attributes. You can
focus on making the edit form more logical and user-friendly for your
editors/clients without any HTML in your way. The template will be a lot
easier to edit in the future.</p>

<p>As for your markup, create another template just for your frontend (HTML
output). You can give it a different name and place it in the same directory
as the default template, but I prefer to create a sub-directory named frontend
and place the file there.</p>

<blockquote
  cite="http://forum.grabaperch.com/forum/02-08-2016-question-regarding-the-reuse-of-a-template-tag-with-same-id-within-a-template#reply-34252"
>
  <p class="attention-seeker">
    At render time, we always need the id and the type attributes.
  </p>
</blockquote>

<p>You will still need Perch tags inside your frontend template, but you can
delete all the attributes from them except the id and type attributes (always
needed).</p>

<p>You can delete all the <code class="language-markup">help</code> and
<code class="language-markup">note-before</code> attributes (that may have
long explanations), <code class="language-markup">order</code> and
<code class="language-markup">divider-before</code> that are also purely for
the edit form. You can also delete any Perch tags you don't need in your
frontend template rather than using the
<code class="language-markup">suppress</code> attribute to hide them.</p>

<p>You'll end up with very short Perch tags and potentially fewer of them. Now
you can scan through your markup much faster.</p>

<p>Below is a Perch tag with the ID of
<code class="language-markup">short_desc</code>. To put into context: it is a
short description of a product. On the product page the editor can add a short
description and a detailed one. So by adding the
<code class="language-markup">help</code> attribute we can give the editors
some tips to differentiate between the two fields.</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 1</span><span style="color: #676E95;">&lt;!-- short_desc in template/shop/products/product.html (the template used for the edit form) --&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 2</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">perch:shop</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 3</span><span style="color: #89DDFF;">  </span><span style="color: #C792EA;">id</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">short_desc</span><span style="color: #89DDFF;">&quot;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 4</span><span style="color: #89DDFF;">  </span><span style="color: #C792EA;">type</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">textarea</span><span style="color: #89DDFF;">&quot;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 5</span><span style="color: #89DDFF;">  </span><span style="color: #C792EA;">label</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">Short Description</span><span style="color: #89DDFF;">&quot;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 6</span><span style="color: #89DDFF;">  </span><span style="color: #C792EA;">editor</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">markitup</span><span style="color: #89DDFF;">&quot;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 7</span><span style="color: #89DDFF;">  </span><span style="color: #C792EA;">markdown</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">true</span><span style="color: #89DDFF;">&quot;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 8</span><span style="color: #89DDFF;">  </span><span style="color: #C792EA;">size</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">s</span><span style="color: #89DDFF;">&quot;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 9</span><span style="color: #89DDFF;">  </span><span style="color: #C792EA;">help</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">This is displayed above the product options. It should be a concise description that highlights the product&#39;s best features</span><span style="color: #89DDFF;">&quot;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">10</span><span style="color: #89DDFF;">  </span><span style="color: #C792EA;">divider-before</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">Product Details</span><span style="color: #89DDFF;">&quot;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">11</span><span style="color: #89DDFF;">/&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">12</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">13</span><span style="color: #676E95;">&lt;!-- short_desc in template/shop/products/frontend/product.html (the template used for front-end) --&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">14</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">perch:shop</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">id</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">short_desc</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">type</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">textarea</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> /&gt;</span></div></code></pre>

<h3>Don't forget to edit your perch_shop_product function:</h3>

<pre><code data-theme="material-theme-palenight" data-lang="php" class='language-php torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #82AAFF;">perch_shop_product</span><span style="color: #89DDFF;">(</span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">my-product</span><span style="color: #89DDFF;">&#39;</span><span style="color: #89DDFF;">,</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">[</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">template</span><span style="color: #89DDFF;">&#39;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">=&gt;</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&#39;</span><span style="color: #C3E88D;">frontend/product.html</span><span style="color: #89DDFF;">&#39;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #89DDFF;">]);</span></div></code></pre>
	]]></description>
</item>            <item>
	<title>Toggle Table Data with CSS</title>
	<pubDate>Wed, 22 Feb 2017 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2017/02/toggle-table-data-with-css/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2017/02/toggle-table-data-with-css/</guid>
	<description><![CDATA[
		<h2>The Problem</h2>

<p>You need to design a table that displays its data in two different units. You
want to avoid displaying both sets at once.</p>

<p>I came across this little problem while working on an e-commerce website. The
client wanted to display the measurements in their size guide in both
centimetres and inches.</p>

<p>The user shouldn’t have to convert the measurements to their unit of
preference. And displaying the measurements in both units at once wasn’t a
very desirable option. Why should the user go through both units when they
only need to see the one they prefer? It’s our job to improve their experience
after all.</p>

<h2>The Solution</h2>

<p>Give the user the option to toggle between both units. If they want to peruse
the size guide in centimetres, they can. If their American friend next to them
needs to see the measurements in inches, they are just a click/tap away.</p>

<p>Using JavaScript is an obvious option. And given many online stores rely on
JavaScript for critical functions (adding items to cart, checkout process,
etc), a non-JavaScript method may not seem necessary here. It is possible
nonetheless.</p>

<blockquote cite="https://twitter.com/heydonworks/status/853213500365209600">
  <p class="attention-seeker">
    Update: based on
    <a href="https://twitter.com/heydonworks/status/853213500365209600"
      >Heydon Pickering's tweet</a>, this method is not great for accessibility. Proceed with caution!
  </p>
</blockquote>

<p>The method is really simple. We'll rely on a checkbox's state
<code class="language-css">.mycheckbox:checked</code> and use CSS's general
sibling selector to target the measurements we want to hide/display.</p>

<h3>Here is what we're after:</h3>

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="pRKdeO" data-user="hus_hmd" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/hus_hmd/pen/pRKdeO">
  Toggle Table Data with CSS (size guide example)</a> by Hussein Al Hammad (<a href="https://codepen.io/hus_hmd">@hus_hmd</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>

<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

<h2>The Code</h2>

<p>A size guide is typically presented in a table. The markup for the table here
is pretty standard. We'll just include two
<code class="language-markup">spans</code> (one for each measurement unit)
inside the table's <code class="language-markup">td</code>s.</p>

<p>In order to be able to target these spans with the general sibling selector
and based on the checkbox's state
<code class="language-css">.checkbox:checked ~ table span</code>, we need to
make the checkbox and the table siblings (i.e. have the same parent element).
We also need to place the checkbox before the table. The checkbox doesn't have
to be placed directly before the table though.</p>

<blockquote
  cite="https://developer.mozilla.org/en/docs/Web/CSS/General_sibling_selectors"
>
  <p class="attention-seeker">
    "The ~ combinator separates two selectors and matches the second element
    only if it is preceded by the first, and both share a common parent."
  </p>
</blockquote>

<h3>Simplified markup</h3>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">input</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">type</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">checkbox</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">id</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">toggle</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> /&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 2</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">table</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 3</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">tbody</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 4</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">tr</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 5</span><span style="color: #A6ACCD;">      </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">td</span><span style="color: #89DDFF;">&gt;</span><span style="color: #A6ACCD;">Small</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">td</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 6</span><span style="color: #A6ACCD;">      </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">td</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 7</span><span style="color: #A6ACCD;">        </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">span</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">class</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">default</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span><span style="color: #A6ACCD;">20 cm</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">span</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 8</span><span style="color: #A6ACCD;">        </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">span</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">class</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">alt</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span><span style="color: #A6ACCD;">8 inch</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">span</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 9</span><span style="color: #A6ACCD;">      </span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">td</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">10</span><span style="color: #A6ACCD;">      </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">td</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">11</span><span style="color: #A6ACCD;">        </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">span</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">class</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">default</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span><span style="color: #A6ACCD;">28 cm</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">span</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">12</span><span style="color: #A6ACCD;">        </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">span</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">class</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">alt</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span><span style="color: #A6ACCD;">11 inch</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">span</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">13</span><span style="color: #A6ACCD;">      </span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">td</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">14</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">tr</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">15</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">tbody</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">16</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">table</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<h3>CSS to toggle spans</h3>

<pre><code data-theme="material-theme-palenight" data-lang="css" class='language-css torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 1</span><span style="color: #FFCB6B;">table</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">default</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 2</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">display</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> inline</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 3</span><span style="color: #89DDFF;">}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 4</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 5</span><span style="color: #FFCB6B;">table</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">alt</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 6</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">display</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> none</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 7</span><span style="color: #89DDFF;">}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 8</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 9</span><span style="color: #676E95;">/*checked*/</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">10</span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">checkbox</span><span style="color: #89DDFF;">:</span><span style="color: #C792EA;">checked</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">~</span><span style="color: #A6ACCD;"> </span><span style="color: #FFCB6B;">table</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">default</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">11</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">display</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> none</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">12</span><span style="color: #89DDFF;">}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">13</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">14</span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">checkbox</span><span style="color: #89DDFF;">:</span><span style="color: #C792EA;">checked</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">~</span><span style="color: #A6ACCD;"> </span><span style="color: #FFCB6B;">table</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">alt</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">15</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">display</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> inline</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">16</span><span style="color: #89DDFF;">}</span></div></code></pre>

<p>Now it functions as intended. It's a table with a checkbox that toggles the
table's data.</p>

<p>By default: we leave the checkbox unchecked and hide
<code class="language-css">.alt</code> spans with CSS. When checked, we hide
the <code class="language-css">.default</code> spans and display the
<code class="language-css">.alt</code> spans</p>

<p>Now all there's left to do is to customise our checkbox to make it look like a
toggle switch and add clear labelling. We want these elements next to each
other in this order:</p>

<ol>
<li>Main label</li>
<li>First (default) measurement unit label</li>
<li>Checkbox/custom toggle switch</li>
<li>Alternative measurement unit label</li>
</ol>

<p>There are many examples of
<a href="https://codepen.io/search/pens?q=switch&limit=all&order=popularity&depth=everything&show_forks=false" target="_blank">custom switches on CodePen</a>. That's a good starting place to get some inspiration.</p>

<h3>When coding your own remember this:</h3>

<p>Firstly, (as far as I know) you can't use pseudo elements on
<code class="language-css">input[type=checkbox]</code>. In my example, I'm
using an empty span for my switch.</p>

<p>The other thing to keep in mind is while you want to hide the checkbox you
need it to remain clickable. You want the user to be actually clicking the
checkbox when they click on the measurement unit label they want to display.
The trick is to set the <code class="language-css">opacity</code> of the
checkbox to 0 (makes it invisible, but remains clickable) and absolutely
position it to overlap with the measurement units labels. You may also need to
set the checkbox's <code class="language-css">z-index</code> to a higher value
to ensure it's on top (and thus remains clickable).</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">label</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">for</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">toggle</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">class</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">toggle__main-label</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span><span style="color: #A6ACCD;">Measurements in: </span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">label</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">label</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">for</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">toggle</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">class</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">toggle__option</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span><span style="color: #A6ACCD;">CM</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">label</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">input</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">type</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">checkbox</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">class</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">toggle__checkbox</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">id</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">toggle</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> /&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">span</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">class</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">toggle__switch</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;&lt;/</span><span style="color: #F07178;">span</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">label</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">for</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">toggle</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">class</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">toggle__option</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span><span style="color: #A6ACCD;">INCHES</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">label</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<pre><code data-theme="material-theme-palenight" data-lang="css" class='language-css torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 1</span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">toggle__main-label</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 2</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">display</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> inline-block</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 3</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">vertical-align</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> middle</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 4</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">margin-right</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">10px</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 5</span><span style="color: #89DDFF;">}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 6</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 7</span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">toggle__checkbox</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 8</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">opacity</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">0</span><span style="color: #89DDFF;">;</span><span style="color: #A6ACCD;"> </span><span style="color: #676E95;">/*invisible but it&#39;s there (we need to keep it clickable)*/</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 9</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">width</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">100%</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">10</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">z-index</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">99</span><span style="color: #89DDFF;">;</span><span style="color: #A6ACCD;"> </span><span style="color: #676E95;">/*make sure it&#39;s on top so it&#39;s clickable*/</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">11</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">position</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> absolute</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">12</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">left</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">50%</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">13</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">transform</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #82AAFF;">translateX</span><span style="color: #89DDFF;">(</span><span style="color: #F78C6C;">-50%</span><span style="color: #89DDFF;">);</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">14</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">cursor</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> pointer</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">15</span><span style="color: #89DDFF;">}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">16</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">17</span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">toggle__option</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">18</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">display</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> inline-block</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">19</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">vertical-align</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> middle</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">20</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">user-select</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> none</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">21</span><span style="color: #89DDFF;">}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">22</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">23</span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">toggle__option</span><span style="color: #89DDFF;">:</span><span style="color: #C792EA;">first-child</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">24</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">text-align</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> right</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">25</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">left</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">0</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">26</span><span style="color: #89DDFF;">}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">27</span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">toggle__option</span><span style="color: #89DDFF;">:</span><span style="color: #C792EA;">last-child</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">28</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">right</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">0</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">29</span><span style="color: #89DDFF;">}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">30</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">31</span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">toggle__switch</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">32</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">display</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> inline-block</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">33</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">vertical-align</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> middle</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">34</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">position</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> relative</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">35</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">width</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">50px</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">36</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">height</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">20px</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">37</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">margin</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">0</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">10px</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">38</span><span style="color: #89DDFF;">}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">39</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">40</span><span style="color: #676E95;">/*focus styles for accessibility*/</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">41</span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">toggle__checkbox</span><span style="color: #89DDFF;">:</span><span style="color: #C792EA;">focus</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">~</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">toggle__switch</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">42</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">outline</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">1px</span><span style="color: #A6ACCD;"> solid </span><span style="color: #89DDFF;">#</span><span style="color: #A6ACCD;">19ca2a</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">43</span><span style="color: #89DDFF;">}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">44</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">45</span><span style="color: #676E95;">/*base of toggle switch*/</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">46</span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">toggle__switch</span><span style="color: #89DDFF;">:</span><span style="color: #C792EA;">before</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">47</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">content</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&quot;&quot;</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">48</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">position</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> absolute</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">49</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">display</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> block</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">50</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">left</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">50%</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">51</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">top</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">50%</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">52</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">transform</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #82AAFF;">translate</span><span style="color: #89DDFF;">(</span><span style="color: #F78C6C;">-50%</span><span style="color: #89DDFF;">,</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">-50%</span><span style="color: #89DDFF;">);</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">53</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">z-index</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">1</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">54</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">background-color</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">#</span><span style="color: #A6ACCD;">464646</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">55</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">width</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">50px</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">56</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">height</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">2px</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">57</span><span style="color: #89DDFF;">}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">58</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">59</span><span style="color: #676E95;">/*move to either side of the base based on checkbox&#39;s state*/</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">60</span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">toggle__switch</span><span style="color: #89DDFF;">:</span><span style="color: #C792EA;">after</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">61</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">content</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">&quot;&quot;</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">62</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">position</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> absolute</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">63</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">display</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> block</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">64</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">background-color</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">#</span><span style="color: #A6ACCD;">171717</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">65</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">z-index</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">1</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">66</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">67</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">width</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">20px</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">68</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">height</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">7px</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">69</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">left</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">50%</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">70</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">top</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">50%</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">71</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">transform</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #82AAFF;">translate</span><span style="color: #89DDFF;">(</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">72</span><span style="color: #A6ACCD;">    </span><span style="color: #F78C6C;">-130%</span><span style="color: #89DDFF;">,</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">73</span><span style="color: #A6ACCD;">    </span><span style="color: #F78C6C;">-50%</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">74</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">);</span><span style="color: #A6ACCD;"> </span><span style="color: #676E95;">/*position to the left side of the switch base*/</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">75</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">transition</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> transform </span><span style="color: #F78C6C;">0.3s</span><span style="color: #A6ACCD;"> ease-out</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">76</span><span style="color: #89DDFF;">}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">77</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">78</span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">toggle__checkbox</span><span style="color: #89DDFF;">:</span><span style="color: #C792EA;">checked</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">~</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">toggle__switch</span><span style="color: #89DDFF;">:</span><span style="color: #C792EA;">after</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">79</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">transform</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #82AAFF;">translate</span><span style="color: #89DDFF;">(</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">80</span><span style="color: #A6ACCD;">    </span><span style="color: #F78C6C;">30%</span><span style="color: #89DDFF;">,</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">81</span><span style="color: #A6ACCD;">    </span><span style="color: #F78C6C;">-50%</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">82</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">);</span><span style="color: #A6ACCD;"> </span><span style="color: #676E95;">/*position to the right side of the switch base*/</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">83</span><span style="color: #89DDFF;">}</span></div></code></pre>

<h2>Last Words</h2>

<p>If you want to get a bit fancy, you can detect the user’s country (e.g. from
user’s IP address with PHP) and display the unit of choice in their country by
default.</p>

<p>Javascript is still an option; you could display the measurements in both
units by default and hide the alternative with Javascript (if the script
loads). This way users who don’t have Javascript enabled will still be able to
view both sets of measurements, and those who have it enabled will be able to
toggle the measurements as they wish. Progressive Enhancement FTW.</p>

<p>I haven’t done any accessibility testing. The checkbox is still accessible via
the keyboard and the focus styles highlights the custom switch. If you think
the checkbox isn’t fully accessible and know how to fix that, please share
your thoughts.</p>
	]]></description>
</item>            <item>
	<title>Create a Discreet &quot;Dropdown&quot; Menu</title>
	<pubDate>Fri, 05 Aug 2016 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2016/08/create-a-discreet-dropdown-menu/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2016/08/create-a-discreet-dropdown-menu/</guid>
	<description><![CDATA[
		<h2>The select Element</h2>

<p>The <code class="language-markup">select</code> element is mainly associated with drop-down menus/lists. That's the default after all. You add a select element to a page and you see a white rectangle with an arrow pointing down at one end. You click on it, and based on the default behaviour you see a drop-down list of options for you to choose from.</p>

<p>This is the default behaviour on desktop browsers (and tablets?), but have you ever tabbed on a <code class="language-markup">select</code> element on a mobile browser? There are 2 behaviours I've seen:</p>

<ol>
<li>Options displayed in a popup at the bottom of the screen (where the keyboard pops up)</li>
<li>Options displayed in a popup at the centre of the screen</li>
</ol>

<p>On mobile browsers, users see a list of options but not in a drop-down list. Tabbing the <code class="language-markup">select</code> element when styled using its default styling (white rectangle with an arrow pointing down) doesn't trigger the expected behaviour based on its visual representation. The user doesn't get a drop-down list.</p>

<p>Besides, the W3C's definition of the <code class="language-markup">select</code> element doesn't explicitly describe it as a drop-down list.</p>

<blockquote cite="https://dev.w3.org/html5/spec-preview/the-select-element.html">
  <p>
    "The select element represents a control for selecting amongst a set of options."
    </p>  
</blockquote>

<p>So styling the <code class="language-markup">select</code> element differently may make sense in some situations. Here's a semantically correct, pure CSS method to do just that.</p>

<h2>What we will do</h2>

<ul>
<li>Remove the default styling of the <code class="language-markup">select</code> element (including the arrow)</li>
<li>Position the <code class="language-markup">label</code> to overlap the <code class="language-markup">select</code> element and make it look clickable without affecting functionality or semantics (in reality the user will be clicking the <code class="language-markup">select</code> element not the <code class="language-markup">label</code>)</li>
</ul>

<iframe height='380' scrolling='no' src='//codepen.io/hus_hmd/embed/mEjBQY/?height=384&theme-id=0&default-tab=result&embed-version=2' frameborder='no' allowtransparency='true' allowfullscreen='true' style='width: 100%;'>
</iframe>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">id</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">wrapper</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">label</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">for</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">select-options</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span><span style="color: #A6ACCD;">Change</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">label</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">select</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">id</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">select-options</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">option</span><span style="color: #89DDFF;">&gt;</span><span style="color: #A6ACCD;">Option 1</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">option</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">option</span><span style="color: #89DDFF;">&gt;</span><span style="color: #A6ACCD;">Option 2</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">option</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">6</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">option</span><span style="color: #89DDFF;">&gt;</span><span style="color: #A6ACCD;">Option 3</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">option</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">7</span><span style="color: #A6ACCD;">  </span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">select</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">8</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">div</span><span style="color: #89DDFF;">&gt;</span></div></code></pre>

<p>All you need is an explicit <code class="language-markup">label</code> and the <code class="language-markup">select</code> element itself (just like you would normally create it).</p>

<p>We need to edit the styling of their parent element. If your markup allows you to apply the below styles without affecting your page's layout, do so. Otherwise, you may need to add a <code class="language-markup">div</code> wrapper.</p>

<p>Now we have an explicit text label using the <code class="language-markup">label</code> element. And we have set the value of the <code class="language-markup">for</code> attribute on the <code class="language-markup">label</code> element to match the value of the <code class="language-markup">id</code> attribute on the <code class="language-markup">select</code> element. This is semantically correct and there shouldn't be accessibility concerns.</p>

<h3>Parent Element</h3>

<pre><code data-theme="material-theme-palenight" data-lang="css" class='language-css torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">wrapper</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">position</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> relative</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">width</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">300px</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #89DDFF;">}</span></div></code></pre>

<p>For the parent element, in this case our <code class="language-markup">div</code> wrapper, we set the <code class="language-css">position</code> property to relative because we will be applying absolute positioning for the <code class="language-markup">label</code>. The width can be whatever suits your layout.</p>

<h3>select Element</h3>

<pre><code data-theme="material-theme-palenight" data-lang="css" class='language-css torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 1</span><span style="color: #FFCB6B;">select</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 2</span><span style="color: #A6ACCD;">  </span><span style="color: #676E95;">/* remove default styles */</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 3</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">background</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> transparent</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 4</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">border</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> none</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 5</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">-webkit-appearance</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> none</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 6</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">-moz-appearance</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> none</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 7</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">appearance</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> none</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 8</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 9</span><span style="color: #A6ACCD;">  </span><span style="color: #676E95;">/* keep on top so it&#39;s clickable */</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">10</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">position</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> relative</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">11</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">z-index</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">99</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">12</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">cursor</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> pointer</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">13</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">14</span><span style="color: #A6ACCD;">  </span><span style="color: #676E95;">/* padding-right to prevent text overlap with label */</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">15</span><span style="color: #A6ACCD;">  </span><span style="color: #676E95;">/*  padding-bottom to assist with :focus styling */</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">16</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">width</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">100%</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">17</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">padding</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">7px</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">70px</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">7px</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">0</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">18</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">19</span><span style="color: #A6ACCD;">  </span><span style="color: #676E95;">/* some browsers support text wrap in select element */</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">20</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">white-space</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> normal</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">21</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">font-size</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">1em</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">22</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">line-height</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">1.35</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">23</span><span style="color: #89DDFF;">}</span></div></code></pre>

<p>Here we remove the default styles of the <code class="language-markup">select</code> element. Setting the <code class="language-css">appearance</code> property to none is what removes the arrow.</p>

<p>Because we will be absolute positioning the <code class="language-markup">label</code> in an overlapping manner, we have to increase the z-index of the <code class="language-markup">select</code> element to make sure the user can interact with it. Setting the cursor to pointer makes it more user friendly on desktop browsers.</p>

<p>Adding some <code class="language-css">padding-right</code> will prevent the text of the <code class="language-markup">select</code> element from overlapping with the <code class="language-markup">label</code>. The <code class="language-css">padding-bottom</code> is needed for the styling of <a href="#accessible-tidy">select:focus</a>.</p>

<p>Setting <code class="language-css">white-space</code> to normal will make the text of the <code class="language-markup">select</code> element wrap to another line if it's too long (doesn't work in all browsers).</p>

<h3>Label</h3>

<pre><code data-theme="material-theme-palenight" data-lang="css" class='language-css torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 1</span><span style="color: #FFCB6B;">label</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 2</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">position</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> absolute</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 3</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">top</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">50%</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 4</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">right</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">0</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 5</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">transform</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #82AAFF;">translateY</span><span style="color: #89DDFF;">(</span><span style="color: #F78C6C;">-50%</span><span style="color: #89DDFF;">);</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 6</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">font-size</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">0.8em</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 7</span><span style="color: #89DDFF;">}</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 8</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 9</span><span style="color: #89DDFF;">.</span><span style="color: #FFCB6B;">wrapper</span><span style="color: #89DDFF;">:</span><span style="color: #C792EA;">hover</span><span style="color: #A6ACCD;"> </span><span style="color: #FFCB6B;">label</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">10</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">color</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">#</span><span style="color: #A6ACCD;">6feaa2</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">11</span><span style="color: #89DDFF;">}</span></div></code></pre>

<p>We vertically centre the <code class="language-markup">label</code> and position it to the right of the parent element.</p>

<p>The aim is to make the user think they can click/tab on the <code class="language-markup">label</code> to select another option, but in reality they are clicking on the <code class="language-markup">select</code> element. So you may want to style it to look like a button.</p>

<p>Note if you want to change the position of the <code class="language-markup">label</code>, just make sure the <code class="language-markup">select</code> element covers the new position (adjust height, width or padding).</p>

<h2>Accessible &amp; Tidy</h2>

<p>By default browsers add an outline to focusable elements such as links and some input elements because of accessibility reasons. The outline provides a visual cue for users who are visually impaired or users who are unable to use a mouse. These users often rely on using the Tab key or assistive technologies to navigate through a page.</p>

<p>Leaving the outline for the <code class="language-markup">select</code> element here causes a problem: when it is clicked or in focus the outline overlaps with the <code class="language-markup">label</code> and it looks a bit messy. Removing it also causes a problem: it makes the page less accessible for some users.</p>

<h3>Alternative :focus Styling</h3>

<pre><code data-theme="material-theme-palenight" data-lang="css" class='language-css torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #FFCB6B;">select</span><span style="color: #89DDFF;">:</span><span style="color: #C792EA;">focus</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">outline</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> none</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">border-bottom</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">2px</span><span style="color: #A6ACCD;"> solid </span><span style="color: #89DDFF;">#</span><span style="color: #A6ACCD;">8eb0e7</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #A6ACCD;">  </span><span style="color: #676E95;">/* decrease padding-bottom by border-bottom-width */</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span><span style="color: #A6ACCD;">  </span><span style="color: #676E95;">/*  This prevents it from affecting the layout */</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">6</span><span style="color: #A6ACCD;">  </span><span style="color: #B2CCD6;">padding-bottom</span><span style="color: #89DDFF;">:</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">5px</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">7</span><span style="color: #89DDFF;">}</span></div></code></pre>

<p>So here we're defining a custom visual cue for this group of users. It's just a <code class="language-css">border-bottom</code>, but should do the trick (choose the colour carefully).</p>

<h2>Uses &amp; Limitations</h2>

<p>The first possible use case is what I discussed at the very start: for mobile devices. You could easily reset the defaults for wider viewports.</p>

<p>Another possible use case is if you want to use the <code class="language-markup">select</code> element not only to provide a control for selecting an option, but also to display the selected option as information. I’ve used it in an <a href="https://codepen.io/hus_hmd/details/OXwgYj" title="Audio Visualisation & Lyrics Display">audio visualisation experiment</a> in which I made it possible for the user to change the streaming track via a <code class="language-markup">select</code> element. I didn’t want to have a <code class="language-markup">select</code> element where the track title is visible and display the track title some where else as well.</p>

<p>There are some limitations though. Given that not all browsers support text wrap in the <code class="language-markup">select</code> element means the options have to be short to use this method. And since the <code class="language-markup">select</code> element and the <code class="language-markup">label</code> have to overlap, the <code class="language-markup">label</code> text needs to be short as well (unless you position it above or below the <code class="language-markup">select</code> element and increase the <code class="language-markup">select</code> element's height or padding).</p>

<p>I have not done any user testing. I have no idea whether this method will generate any confusion to some users. On the other hand, most users are already familiar with the default styling of the <code class="language-markup">select</code> element, so when they see it they know there are options to choose from straight away.</p>

<p>Your choice of the <code class="language-markup">label</code> text has to be made carefully. Using verbs like 'change' or 'choose' will make it clearer to the user that there is some interaction required.</p>

<h2>Browser Support</h2>

<p>This solution relies on setting the <code class="language-css">appearance</code> property value to none which is supported in most browsers with vendor prefixes. IE desktop doesn't support it all, but surprisingly IE Mobile 11 does!</p>

<p>Check <a href="https://caniuse.com/#search=appearance" title="Can I Use CSS appearance property?">browser support</a></p>
	]]></description>
</item>            <item>
	<title>Smooth Scrolling with Just 1.1KB</title>
	<pubDate>Sun, 24 Jul 2016 00:00:00 +0000</pubDate>
	<link>https://hussein-alhammad.com/blog/2016/07/smooth-scrolling-with-just-1-1kb/</link>
	<guid isPermaLink="true">https://hussein-alhammad.com/blog/2016/07/smooth-scrolling-with-just-1-1kb/</guid>
	<description><![CDATA[
		<p>If you are already using a JavaScript library for your animations (e.g.
<a href="http://velocityjs.org/">Velocity.js</a>) on your
project, chances are it has a function that allows you to smooth scroll within
the page and possibly even within scrollable containers. So you could just use
it to add smooth scrolling.</p>

<p>But there are cases where you find yourself working on a project that doesn’t
require much JavaScript to justify using Velocity.js (34KB) (or even worse
jQuery (84KB)) just to add smooth scrolling. The thing is you don’t have to
because there is a very tiny JavaScript module called
<a href="https://zengabor.github.io/zenscroll/">Zenscroll</a>.</p>

<h2>Zenscroll</h2>

<p>I came across Zenscroll recently and it quickly became my go-to scrolling
module. What’s not to love:</p>

<ul>
<li>Tiny: only ~2KB minified, and only 1.1KB gzipped</li>
<li>Has no dependencies</li>
<li>Awesome features</li>
<li>Good browser support</li>
</ul>

<p>I can already hear you going “holy crap that’s awesome”, but wait there’s
more!</p>

<p>The most convenient feature Zenscroll provides is automatic smooth-scolling on
links within the same page (can be disabled). So if you literally only want to
add smooth scrolling to same page links, then you literally only need to
include Zenscroll in your HTML and you’re done. There’s no need to write any
JavaScript yourself unless you want to <a href="https://zengabor.github.io/zenscroll/#9.changesettings">change the default parameters</a>.</p>

<h3>Other useful features</h3>

<ul>
<li><p>Smooth animated scrolling, using the browser’s built-in smooth-behavior if
it’s enabled.</p></li>
<li><p>Scroll to the top of a specific element.</p></li>
<li><p>Scrolling an element into view, making sure both top &amp; bottom are
visible, if possible.</p></li>
<li><p>Scroll to an element and center it on the screen.</p></li>
<li>Customize the duration of the individual scroll operations.</li>
<li><p>If you provide a callback function it will be executed when the scrolling is
done.</p></li>
<li><p>Specify the spacing between the element and the edge of the screen (e.g.,
for fixed navigation bars and footers).</p></li>
</ul>

<p>Visit Zenscroll’s
<a href="https://zengabor.github.io/zenscroll/#gettingstarted"
    title="Zenscroll's Demo Page">Demo Page</a> to learn how to use it</p>

<p>However, there’s one small bug I encountered whilst playing around with it.</p>

<p>The bug discussed below is from an old version of Zenscroll</p>

<h2>Problem with Disabling Automatic Smoothing on Local Links</h2>

<p>By default Zenscroll takes care of smooth scrolling on local links
automatically and that’s reasonable. And you can easily change the default
parameters for the automatic smoothing (and all other scrolling):</p>

<pre><code data-theme="material-theme-palenight" data-lang="js" class='language-js torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #C792EA;">var</span><span style="color: #A6ACCD;"> defaultDuration </span><span style="color: #89DDFF;">=</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">777</span><span style="color: #89DDFF;">;</span><span style="color: #A6ACCD;"> </span><span style="color: #676E95;">// ms</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #C792EA;">var</span><span style="color: #A6ACCD;"> edgeOffset </span><span style="color: #89DDFF;">=</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">42</span><span style="color: #89DDFF;">;</span><span style="color: #A6ACCD;"> </span><span style="color: #676E95;">// px</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #A6ACCD;">zenscroll</span><span style="color: #89DDFF;">.</span><span style="color: #82AAFF;">setup</span><span style="color: #A6ACCD;">(defaultDuration</span><span style="color: #89DDFF;">,</span><span style="color: #A6ACCD;"> edgeOffset)</span><span style="color: #89DDFF;">;</span></div></code></pre>

<p>You can disable automatic scrolling all together (why would you?) or on
specific links (possible scenario: adding a callback function on scroll
completion).</p>

<p>Unfortunately, I found in some browsers when I disable automatic scrolling and
add my own Zenscroll behaviour to a click event on a local link, the page
jumps to the expected target (no scrolling) then jumps back to the link that
was clicked before executing the desired custom Zenscroll behaviour. The
problem doesn’t happen every single time, but it was certainly repeatable.
This is really the only drawback I encountered with Zenscroll.</p>

<p>Note that this problem doesn’t occur when you use Zenscroll programmatically
on elements other than local links.</p>

<h2>Callback Function on Local Links with Zenscroll</h2>

<p>To add a callback function on scroll completion you normally do this:</p>

<pre><code data-theme="material-theme-palenight" data-lang="js" class='language-js torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #A6ACCD;">zenscroll</span><span style="color: #89DDFF;">.</span><span style="color: #82AAFF;">to</span><span style="color: #A6ACCD;">(element</span><span style="color: #89DDFF;">,</span><span style="color: #A6ACCD;"> duration</span><span style="color: #89DDFF;">,</span><span style="color: #A6ACCD;"> onDone)</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #676E95;">//example</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #A6ACCD;">zenscroll</span><span style="color: #89DDFF;">.</span><span style="color: #82AAFF;">to</span><span style="color: #A6ACCD;">(element</span><span style="color: #89DDFF;">,</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">600</span><span style="color: #89DDFF;">,</span><span style="color: #A6ACCD;"> </span><span style="color: #C792EA;">function</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">()</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span><span style="color: #F07178;">  </span><span style="color: #A6ACCD;">console</span><span style="color: #89DDFF;">.</span><span style="color: #82AAFF;">log</span><span style="color: #F07178;">(</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">scroll complete</span><span style="color: #89DDFF;">&quot;</span><span style="color: #F07178;">)</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">6</span><span style="color: #89DDFF;">}</span><span style="color: #A6ACCD;">)</span><span style="color: #89DDFF;">;</span></div></code></pre>

<p>But given the problem with local links outlined above, you can use a simple
workaround: keep the automatic smoothing and use
<code class="language-javascript">setTimeOut()</code> to execute your code on
scroll completion. Why? Because you know the duration of the scroll; by
default it's <code class="language-javascript">999ms</code> or you can set the
default value yourself with
<code class="language-javascript">zenscroll.setup()</code></p>

<h3>Example:</h3>

<p>You have a comment/reply link. When clicked it takes you to the comment field
on the same page. And on scroll completion you want to give focus to the
comment field (<code class="language-markup">textarea</code> element)</p>

<p>Below is a simplified example and not a fully functioning solution</p>

<pre><code data-theme="material-theme-palenight" data-lang="html" class='language-html torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 1</span><span style="color: #676E95;">&lt;!-- comment --&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 2</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">a</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">href</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">#commentField</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">class</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">reply</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;</span><span style="color: #A6ACCD;">Reply</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">a</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 3</span><span style="color: #676E95;">&lt;!-- more comments --&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 4</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 5</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 6</span><span style="color: #A6ACCD;">.</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 7</span><span style="color: #A6ACCD;">.</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 8</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number"> 9</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">form</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">10</span><span style="color: #A6ACCD;">    </span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">textarea</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">id</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">commentField</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;&lt;/</span><span style="color: #F07178;">textarea</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">11</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">form</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">12</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">13</span><span style="color: #A6ACCD;">.</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">14</span><span style="color: #A6ACCD;">.</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">15</span><span style="color: #89DDFF;">&lt;</span><span style="color: #F07178;">script</span><span style="color: #89DDFF;"> </span><span style="color: #C792EA;">src</span><span style="color: #89DDFF;">=</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">js/zenscroll-min.js</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">&gt;&lt;/</span><span style="color: #F07178;">script</span><span style="color: #89DDFF;">&gt;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">16</span><span style="color: #89DDFF;">&lt;/</span><span style="color: #F07178;">body</span><span style="color: #89DDFF;">&gt;</span><span style="color: #A6ACCD;">&lt;</span></div></code></pre>

<h3>Add eventListener to all 'Reply' links</h3>

<pre><code data-theme="material-theme-palenight" data-lang="js" class='language-js torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #C792EA;">var</span><span style="color: #A6ACCD;"> replies </span><span style="color: #89DDFF;">=</span><span style="color: #A6ACCD;"> document</span><span style="color: #89DDFF;">.</span><span style="color: #82AAFF;">getElementsByClassName</span><span style="color: #A6ACCD;">(</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">reply</span><span style="color: #89DDFF;">&quot;</span><span style="color: #A6ACCD;">)</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span>&nbsp;</div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #89DDFF;">for</span><span style="color: #A6ACCD;"> (</span><span style="color: #C792EA;">var</span><span style="color: #A6ACCD;"> i </span><span style="color: #89DDFF;">=</span><span style="color: #A6ACCD;"> </span><span style="color: #F78C6C;">0</span><span style="color: #89DDFF;">;</span><span style="color: #A6ACCD;"> i </span><span style="color: #89DDFF;">&lt;</span><span style="color: #A6ACCD;"> replies</span><span style="color: #89DDFF;">.</span><span style="color: #A6ACCD;">length</span><span style="color: #89DDFF;">;</span><span style="color: #A6ACCD;"> i</span><span style="color: #89DDFF;">++</span><span style="color: #A6ACCD;">) </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #F07178;">  </span><span style="color: #A6ACCD;">replies</span><span style="color: #F07178;">[</span><span style="color: #A6ACCD;">i</span><span style="color: #F07178;">]</span><span style="color: #89DDFF;">.</span><span style="color: #82AAFF;">addEventListener</span><span style="color: #F07178;">(</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">click</span><span style="color: #89DDFF;">&quot;</span><span style="color: #89DDFF;">,</span><span style="color: #F07178;"> </span><span style="color: #A6ACCD;">prepareReply</span><span style="color: #F07178;">)</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span><span style="color: #89DDFF;">}</span></div></code></pre>

<h3>Add focus to comment field on scroll completion</h3>

<pre><code data-theme="material-theme-palenight" data-lang="js" class='language-js torchlight' style='background-color: #292D3E; --theme-selection-background: #00000080;'><!-- Syntax highlighted by torchlight.dev --><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">1</span><span style="color: #C792EA;">function</span><span style="color: #A6ACCD;"> </span><span style="color: #82AAFF;">prepareReply</span><span style="color: #89DDFF;">(</span><span style="color: #A6ACCD;">e</span><span style="color: #89DDFF;">)</span><span style="color: #A6ACCD;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">2</span><span style="color: #F07178;">  </span><span style="color: #C792EA;">var</span><span style="color: #F07178;"> </span><span style="color: #A6ACCD;">commentField</span><span style="color: #F07178;"> </span><span style="color: #89DDFF;">=</span><span style="color: #F07178;"> </span><span style="color: #A6ACCD;">document</span><span style="color: #89DDFF;">.</span><span style="color: #82AAFF;">getElementsById</span><span style="color: #F07178;">(</span><span style="color: #89DDFF;">&quot;</span><span style="color: #C3E88D;">commentField</span><span style="color: #89DDFF;">&quot;</span><span style="color: #F07178;">)</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">3</span><span style="color: #F07178;">  </span><span style="color: #82AAFF;">setTimeout</span><span style="color: #F07178;">(</span><span style="color: #C792EA;">function</span><span style="color: #F07178;"> </span><span style="color: #89DDFF;">()</span><span style="color: #F07178;"> </span><span style="color: #89DDFF;">{</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">4</span><span style="color: #F07178;">    </span><span style="color: #A6ACCD;">commentField</span><span style="color: #89DDFF;">.</span><span style="color: #82AAFF;">focus</span><span style="color: #F07178;">()</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">5</span><span style="color: #F07178;">  </span><span style="color: #89DDFF;">},</span><span style="color: #F07178;"> </span><span style="color: #F78C6C;">999</span><span style="color: #F07178;">)</span><span style="color: #89DDFF;">;</span></div><div class='line'><span style="color:#3A3F58; text-align: right; -webkit-user-select: none; user-select: none;" class="line-number">6</span><span style="color: #89DDFF;">}</span></div></code></pre>

<h2>Last Words</h2>

<p>I’m so thankful that
<a href="http://www.gaborlenard.com/" title="Gabor Lenard">Gabor Lenard</a>
has released Zenscroll into the public domain. Despite the small bug with
local links, it is immensely powerful for a 1.1KB module.</p>

<p>Now I’m just waiting for the day I enter a stackoverflow question on smooth
scrolling and read “just use Zenscroll” among the answers. Because it is
indeed the “One JavaScript to Smooth-Scroll Them All”.</p>
	]]></description>
</item>    	</channel>
</rss>