<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[This week I learnt...]]></title><description><![CDATA[I like problem solving and making things pretty. I also like red.]]></description><link>https://blog.nicm42.me.uk</link><generator>RSS for Node</generator><lastBuildDate>Mon, 13 Apr 2026 12:21:56 GMT</lastBuildDate><atom:link href="https://blog.nicm42.me.uk/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Trying AI autocomplete]]></title><description><![CDATA[I've used autocomplete before in coding - after all, why type more than you need to? But recently I downloaded Cursor and tried out the AI autocomplete, which can add many lines at a time. And did I f]]></description><link>https://blog.nicm42.me.uk/trying-ai-autocomplete</link><guid isPermaLink="true">https://blog.nicm42.me.uk/trying-ai-autocomplete</guid><category><![CDATA[AI]]></category><dc:creator><![CDATA[Nic]]></dc:creator><pubDate>Sun, 12 Apr 2026 16:08:19 GMT</pubDate><content:encoded><![CDATA[<p>I've used autocomplete before in coding - after all, why type more than you need to? But recently I downloaded Cursor and tried out the AI autocomplete, which can add many lines at a time. And did I find it useful? Well, yes and no.</p>
<p>I've only tried it on a personal project. I do those because I have a fun idea and/or want to try a new language/framework and/or want to remind myself how to use an old one. The first thing it did was to guess correctly what I wanted to do with some data. Which was exciting, but also it wasn't something I do often in that language, so I sort of wanted to remember for myself how to do it. But if it was something I did a lot then I can see how it would be really good to save myself a lot of typing.</p>
<p>The next thing it did was also helpful, except that it had an error on it. It was exactly the sort of error I'd also make, but I'd expected it to know better. Which I know is silly because it's just getting data from people who are also making the same mistake.</p>
<p>After that its guesses about what I was trying to do were completely wrong and it was just annoying. But I've just been working on JavaScript at the moment. It'll be interesting to see how it does when I come to the CSS.</p>
<p>In summary, I can see how it would be really useful at work, when it has all the context for what you're working on and the languages used. And it's all things you do every day and have a deadline on. But on personal projects the one line autocomplete is probably all I need - after all, I don't have a deadline on personal projects, I'm just enjoying working on them.</p>
]]></content:encoded></item><item><title><![CDATA[Moving elements with JavaScript]]></title><description><![CDATA[Recently I had a situation where there were two elements, A and B, each of which contained a whole load of things. On mobile A was at the top and B was below it. But on desktop B was on the left and A]]></description><link>https://blog.nicm42.me.uk/moving-elements-with-javascript</link><guid isPermaLink="true">https://blog.nicm42.me.uk/moving-elements-with-javascript</guid><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Nic]]></dc:creator><pubDate>Mon, 02 Mar 2026 17:39:42 GMT</pubDate><content:encoded><![CDATA[<p>Recently I had a situation where there were two elements, A and B, each of which contained a whole load of things. On mobile A was at the top and B was below it. But on desktop B was on the left and A was on the right.</p>
<p>Theoretically that's easy to fix - flexbox will sort that right out. However, that then meant that the focus order was wrong.</p>
<p>But that's still an easy fix - you just duplicate one of the elements in the HTML and hide one on mobile and the other on desktop. Except that this is in a CMS and the HTML was being built by it and didn't exist as HTML to change. And there were a load of scripts doing things inside these elements, some of which could well depend on a unique ID or a class only being used once. It was an important page on the site, so if I broke it, things would go very wrong, so I didn't want to touch it.</p>
<p>There will one day be some CSS to tackle this: <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/reading-flow">reading-flow</a>. But at the moment it's only available in Chromium browsers and is also marked as experimental, therefore subject to change and shouldn't be used in production code.</p>
<p>Fortunately, there's a really simple JavaScript solution. What I did at desktop size was to move element B to be before element A. When you look at the resulting HTML it looks like it's always been that way round. And the focus goes through the element on the left, followed by the element on the right.</p>
<p>The solution is <code>before</code>. Or <code>after</code>. Since I only have two elements they effectively do the same thing.</p>
<p>To move element B before element A you use:</p>
<p><code>a.before(b)</code></p>
<p>or you can use</p>
<p><code>b.after(a)</code></p>
<p>Which is a bit confusing because if you read that from left to right it sounds like you're saying a is before b, or b is after a. But when you try it it's easy to tell you've done it wrong.</p>
]]></content:encoded></item><item><title><![CDATA[Transitioning display: none]]></title><description><![CDATA[I recently had cause to transition display: none. It’s not yet working in all browsers, but when I tried it I found the effect was so subtle that no one would notice the difference. In this example I had to change the transition time to 1 second in o...]]></description><link>https://blog.nicm42.me.uk/transitioning-display-none</link><guid isPermaLink="true">https://blog.nicm42.me.uk/transitioning-display-none</guid><category><![CDATA[CSS]]></category><dc:creator><![CDATA[Nic]]></dc:creator><pubDate>Sat, 22 Nov 2025 14:25:37 GMT</pubDate><content:encoded><![CDATA[<p>I recently had cause to transition display: none. It’s not yet working in all browsers, but when I tried it I found the effect was so subtle that no one would notice the difference. In this example I had to change the transition time to 1 second in order to see it.</p>
<h2 id="heading-the-html">The HTML</h2>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Some text here<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span>Toggle active state<span class="hljs-tag">&lt;/<span class="hljs-name">button</span></span>
</code></pre>
<h2 id="heading-some-js-to-add-the-transition">Some JS to add the transition</h2>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> button = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'button'</span>);
<span class="hljs-keyword">const</span> div = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'div'</span>);

button.addEventListener(<span class="hljs-string">'click'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
  div.classList.toggle(<span class="hljs-string">'is-active'</span>);
})
</code></pre>
<h2 id="heading-the-css">The CSS</h2>
<pre><code class="lang-css"><span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">align-items</span>: flex-start;
  <span class="hljs-attribute">gap</span>: <span class="hljs-number">20px</span>;
}

<span class="hljs-selector-tag">div</span> <span class="hljs-selector-tag">p</span> {
  <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">4px</span>;
}

<span class="hljs-selector-tag">div</span> <span class="hljs-selector-tag">span</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">4px</span>;
  <span class="hljs-attribute">background</span>: red;
  <span class="hljs-attribute">display</span>: none;
  <span class="hljs-attribute">opacity</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">transition</span>: opacity <span class="hljs-number">1s</span>, display <span class="hljs-number">1s</span> allow-discrete;
}

<span class="hljs-selector-tag">div</span><span class="hljs-selector-class">.is-active</span> <span class="hljs-selector-tag">span</span> {
  <span class="hljs-attribute">display</span>: block;
  <span class="hljs-attribute">opacity</span>: <span class="hljs-number">1</span>;

  @starting-style {
    <span class="hljs-attribute">opacity</span>: <span class="hljs-number">0</span>;
  }
}

<span class="hljs-selector-tag">button</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.5rem</span> <span class="hljs-number">1rem</span>;
}
</code></pre>
<h2 id="heading-the-transition">The transition</h2>
<p>A lot of that CSS is the set up, the transition bits we’re interested in are these bits:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">div</span> <span class="hljs-selector-tag">span</span> {
  <span class="hljs-attribute">display</span>: none;
  <span class="hljs-attribute">opacity</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">transition</span>: opacity <span class="hljs-number">1s</span>, display <span class="hljs-number">1s</span> allow-discrete;
}

<span class="hljs-selector-tag">div</span><span class="hljs-selector-class">.is-active</span> <span class="hljs-selector-tag">span</span> {
  <span class="hljs-attribute">display</span>: block;
  <span class="hljs-attribute">opacity</span>: <span class="hljs-number">1</span>;

  @starting-style {
    <span class="hljs-attribute">opacity</span>: <span class="hljs-number">0</span>;
  }
}
</code></pre>
<p>Most of this is pretty standard. The span starts off with an opacity of 0 and display: none. The transition transitions opacity in 1s. And also display - which works because of the <code>allow-discrete</code> keyword.</p>
<p>And then when the is-active class is applied to the parent div we change it to be an opacity of 1 and display: block.</p>
<p>The thing that actually makes this all work is this bit:</p>
<pre><code class="lang-css"><span class="hljs-keyword">@starting-style</span> {
  <span class="hljs-selector-tag">opacity</span>: 0;
}
</code></pre>
<p>This tells it that it’s starting from an opacity of 0. You’d think it would know that because I told it it had an opacity of 0 when the is-active class is applied. But it needs to know within the is-active class too, so it knows how to transition the opacity in. If you remove this it will transition out, but not in. It’s as if now the is-active class is applied it can no longer see the styles set on the div and you have to let it know what they are.</p>
<p>Codepen decided not to run JS when I set this up, so here’s a <a target="_blank" href="https://jsfiddle.net/sytk9L73/1/">JSFiddle link</a> instead.</p>
]]></content:encoded></item><item><title><![CDATA[Styling sibling hover and none hover]]></title><description><![CDATA[I recently had some complicated CSS to get my head around. I have a list of links which I want to style when they’re being hovered/focus on. But, if none of them are being hovered or focused, then I want to style the first one in the list.
The HTML
<...]]></description><link>https://blog.nicm42.me.uk/styling-sibling-hover-and-none-hover</link><guid isPermaLink="true">https://blog.nicm42.me.uk/styling-sibling-hover-and-none-hover</guid><category><![CDATA[CSS]]></category><dc:creator><![CDATA[Nic]]></dc:creator><pubDate>Sun, 14 Sep 2025 11:26:36 GMT</pubDate><content:encoded><![CDATA[<p>I recently had some complicated CSS to get my head around. I have a list of links which I want to style when they’re being hovered/focus on. But, if none of them are being hovered or focused, then I want to style the first one in the list.</p>
<h2 id="heading-the-html">The HTML</h2>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>&gt;</span>One<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>&gt;</span>Two<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>&gt;</span>Three<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>&gt;</span>Four<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>&gt;</span>Five<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>&gt;</span>Six<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
</code></pre>
<h2 id="heading-styling-the-hoverfocus">Styling the hover/focus</h2>
<p>This is the simplest part - I am making the background of the links pink when they are hovered or focused.</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">li</span> <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span>,
<span class="hljs-selector-tag">li</span> <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:focus</span> {
  <span class="hljs-attribute">background-color</span>: pink;
}
</code></pre>
<h2 id="heading-styling-the-first-element">Styling the first element</h2>
<p>Also, simple.</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">li</span><span class="hljs-selector-pseudo">:first-of-type</span> <span class="hljs-selector-tag">a</span> {
  <span class="hljs-attribute">background-color</span>: pink;
}
</code></pre>
<h2 id="heading-styling-the-siblings">Styling the siblings</h2>
<p>If we just make the hover/focus pink and the first element pink when we aren’t hovering/focusing then the first element is pink which is what we want. But then it keeps being pink even when we’re hovering/focusing on something else. What we need to do is to unstyle the siblings when one is being hovered/focused.</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">li</span><span class="hljs-selector-pseudo">:has(</span>~ <span class="hljs-selector-tag">li</span> <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover)</span> <span class="hljs-selector-tag">a</span>,
<span class="hljs-selector-tag">li</span><span class="hljs-selector-pseudo">:has(</span>~ <span class="hljs-selector-tag">li</span> <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:focus)</span> <span class="hljs-selector-tag">a</span> {
  <span class="hljs-attribute">background-color</span>: unset;
}
</code></pre>
<p>This is saying that if an <code>li</code> has a sibling with an <code>a</code> that is being hovered/focused, then style this <code>a</code>.</p>
<h2 id="heading-downside">Downside</h2>
<p>There is a downside to this. If you hover over an element, come off it so you’re not hovering on anything, then hover on another element shortly after, then the background jumps to the first one in between, which looks a bit odd.</p>
<h2 id="heading-final-code">Final code</h2>
<p>Here is the whole code:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codepen.io/nicm42/pen/LEpgqRd">https://codepen.io/nicm42/pen/LEpgqRd</a></div>
]]></content:encoded></item><item><title><![CDATA[The scripting media query]]></title><description><![CDATA[I know there are a lot more media queries out there than the ones I usually use, but this week I used one I’d never come across before.
The scripting media query checks whether JavaScript is enabled. It has three keywords, although only two of them a...]]></description><link>https://blog.nicm42.me.uk/the-scripting-media-query</link><guid isPermaLink="true">https://blog.nicm42.me.uk/the-scripting-media-query</guid><category><![CDATA[CSS]]></category><category><![CDATA[Tailwind CSS]]></category><dc:creator><![CDATA[Nic]]></dc:creator><pubDate>Sun, 31 Aug 2025 16:46:11 GMT</pubDate><content:encoded><![CDATA[<p>I know there are a lot more media queries out there than the ones I usually use, but this week I used one I’d never come across before.</p>
<p>The scripting media query checks whether JavaScript is enabled. It has three keywords, although only two of them are useful.</p>
<pre><code class="lang-css"><span class="hljs-keyword">@media</span> (<span class="hljs-attribute">scripting:</span> none) {
    // <span class="hljs-selector-tag">Javascript</span> <span class="hljs-selector-tag">is</span> <span class="hljs-selector-tag">disabled</span>
}

<span class="hljs-keyword">@media</span> (<span class="hljs-attribute">scripting:</span> initial-<span class="hljs-keyword">only</span>) {
    // <span class="hljs-selector-tag">Javascript</span> <span class="hljs-selector-tag">is</span> <span class="hljs-selector-tag">only</span> <span class="hljs-selector-tag">enabled</span> <span class="hljs-selector-tag">on</span> <span class="hljs-selector-tag">page</span> <span class="hljs-selector-tag">load</span>
}

<span class="hljs-keyword">@media</span> (<span class="hljs-attribute">scripting:</span> enabled) {
    // <span class="hljs-selector-tag">Javascript</span> <span class="hljs-selector-tag">is</span> <span class="hljs-selector-tag">enabled</span>
}
</code></pre>
<p>(I know CSS doesn’t use // for comments, but actual comments are grey, which I find a very hard colour to read on a black background).</p>
<p>Interestingly, Tailwind (which was what the project was using) only has the disabled version (with <code>noscript:classHere</code>), which was unhelpful for me. In my case I had a sticky header which shrinks down on scroll. The shrinking can only be done with JavaScript and without shrinking the header is massive. So if JavaScript is disabled we don’t want a sticky header. Which is easier to do in CSS than Tailwind:</p>
<pre><code class="lang-css"><span class="hljs-keyword">@media</span> (<span class="hljs-attribute">scripting:</span> enabled) {
    <span class="hljs-selector-tag">header</span> {
        <span class="hljs-attribute">position</span>: sticky;
        <span class="hljs-attribute">top</span>: <span class="hljs-number">0</span>;
    }
}
</code></pre>
<p>In Tailwind I’d have needed to do:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">header</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"sticky top-0 noscript:static"</span>&gt;</span>
    Content here
<span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Using ChatGPT]]></title><description><![CDATA[This week I had a couple of things I got stuck on and googling/Stack Overflow weren’t helping me much. I know that people have said that AI can help you with programming and I thought I’d see how much help it was getting me unstuck.
Problem 1
I went ...]]></description><link>https://blog.nicm42.me.uk/using-chatgpt</link><guid isPermaLink="true">https://blog.nicm42.me.uk/using-chatgpt</guid><category><![CDATA[AI]]></category><dc:creator><![CDATA[Nic]]></dc:creator><pubDate>Fri, 23 May 2025 18:45:37 GMT</pubDate><content:encoded><![CDATA[<p>This week I had a couple of things I got stuck on and googling/Stack Overflow weren’t helping me much. I know that people have said that AI can help you with programming and I thought I’d see how much help it was getting me unstuck.</p>
<h2 id="heading-problem-1">Problem 1</h2>
<p>I went a few rounds with it coming up with suggestion that sounded reasonable, but none of them helped. Although I found that telling it exactly what wasn’t working helped me to establish what the problem actually was.</p>
<p>Sadly, though, it then started giving me code where I couldn’t see what it had changed. When I asked, it told me it had added a function. But that function was already there… At that point it become clear that it wasn’t going to be of any more help and I gave up.</p>
<h2 id="heading-problem-2">Problem 2</h2>
<p>This started similarly, with it coming up with reasonable-sounding explanations and suggestions, none of which solved the problem. And then it started giving me links to CodePens it had created. All of which were empty. When I asked it apologised and gave me a new link. Which was also empty. It did also give me all the code that would be in the CodePen, so it didn’t matter too much.</p>
<p>But since we weren’t solving the problem I asked it where it had got its answers from. It initially refused to give me any links until I persisted. And then it gave me StackOverflow links to completely different problems in completely different languages…</p>
<p>When I called it on that it told me what to search for on StackOverflow (and how to search StackOverflow). They were reasonable search strings, even if they didn’t lead to to anything I hadn’t already come across.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>If I was merely judging it on those two interactions then I’d say it was completely useless. Plus every time it gives an answer it started by telling me I was right which got grating after a while. But I already knew it did that sort of thing and can’t be turned off. I also hate that it shows you its answer one line at a time, but I’d already asked it about that and it also can’t be turned off.</p>
<p>So it’s annoying, but I’m not totally giving up on it. I have heard from people that it can be good for giving you starter code that you can improve on. And there may be less complex problems that it will be better at finding answers for.</p>
<p>I also only tried ChatGPT - it may be that some of the others are less annoying and/or more useful.</p>
]]></content:encoded></item><item><title><![CDATA[Select keyboard controls]]></title><description><![CDATA[I was recently given a design that included a customised select. Since that’s currently only available in the most recent version of Chrome, I went looking for libraries and instructions on how to create your own select. And I came across a problem: ...]]></description><link>https://blog.nicm42.me.uk/select-keyboard-controls</link><guid isPermaLink="true">https://blog.nicm42.me.uk/select-keyboard-controls</guid><category><![CDATA[a11y]]></category><dc:creator><![CDATA[Nic]]></dc:creator><pubDate>Fri, 18 Apr 2025 15:35:15 GMT</pubDate><content:encoded><![CDATA[<p>I was recently given a design that included a customised select. Since that’s currently only available in the most recent version of Chrome, I went looking for libraries and instructions on how to create your own select. And I came across a problem: none of them talked about the keyboard controls in a way that matched with how select works for me with the keyboard. Until I considered that I was using Firefox and most people use Chrome. Turns out they don’t work identically. So I went through the main browsers and documented what did what.</p>
<h2 id="heading-same-for-all-browsers">Same for all browsers</h2>
<p>Most keyboard controls are the same in all browsers.</p>
<h3 id="heading-when-select-is-closed">When select is closed:</h3>
<p><strong>Enter</strong>: does nothing<br /><strong>Space</strong>: opens select<br /><strong>Tab</strong>: goes to next tabbable element<br /><strong>Shift + tab</strong>: goes to previous tabbable element<br /><strong>Down arrow</strong>: opens select<br /><strong>Up arrow</strong>: opens select<br /><strong>Any letter key</strong>: selects first/next option that starts with that letter</p>
<h3 id="heading-when-select-is-open">When select is open:</h3>
<p><strong>Enter</strong>: selects focused option and closes select<br /><strong>Escape</strong>: closes select<br /><strong>Down arrow</strong>: focuses next option<br /><strong>Up arrow</strong>: focuses previous option<br /><strong>Home</strong>: focuses option at top of list<br /><strong>End</strong>: focuses option at bottom of list<br /><strong>Any letter key</strong>: selects first/next option that starts with that letter</p>
<h2 id="heading-different-in-different-browsers">Different in different browsers</h2>
<p>I tested in Firefox, Chrome, Safari and Opera, all on a Mac. Although a Mac has different keyboard shortcuts to Windows and Linux, navigating a website doesn’t seem to be any different on a Mac.</p>
<h3 id="heading-when-select-is-closed-1">When select is closed</h3>
<p><strong>Escape</strong><br />Firefox: goes to next tabbable element<br />The rest: does nothing</p>
<p><strong>Page down</strong><br />Firefox: selects option one page down<br />The rest: does nothing</p>
<p><strong>Page up</strong><br />Firefox: selects option one page up<br />The rest: does nothing</p>
<p><strong>Home</strong><br />Firefox: selects option at top of list<br />The rest: does nothing</p>
<p><strong>End</strong><br />Firefox: selects option at bottom of list<br />The rest: does nothing</p>
<h3 id="heading-when-select-is-open-1">When select is open</h3>
<p><strong>Space</strong><br />Firefox: does nothing<br />The rest: selects focused option and closes select</p>
<p><strong>Tab</strong><br />Firefox: selects focused option and closes select<br />The rest: selects random option</p>
<p><strong>Shift + tab</strong><br />Firefox: selects focused option and closes select<br />The rest: selects random option</p>
<p><strong>Page down</strong><br />Firefox: focuses option one page down<br />The rest: focuses option at bottom of list</p>
<p><strong>Page up</strong><br />Firefox: focuses option one page up<br />The rest: focuses option at top of list</p>
]]></content:encoded></item><item><title><![CDATA[Accessible toggle]]></title><description><![CDATA[I recently got given a design that included a toggle. But I know they are not the most accessible of components, so I had to do some research on the best ways to create them.
I started at https://www.w3.org/WAI/ARIA/apg/patterns/switch/. This site is...]]></description><link>https://blog.nicm42.me.uk/accessible-toggle</link><guid isPermaLink="true">https://blog.nicm42.me.uk/accessible-toggle</guid><category><![CDATA[Accessibility]]></category><category><![CDATA[CSS]]></category><dc:creator><![CDATA[Nic]]></dc:creator><pubDate>Wed, 12 Feb 2025 19:16:15 GMT</pubDate><content:encoded><![CDATA[<p>I recently got given a design that included a toggle. But I know they are not the most accessible of components, so I had to do some research on the best ways to create them.</p>
<p>I started at <a target="_blank" href="https://www.w3.org/WAI/ARIA/apg/patterns/switch/">https://www.w3.org/WAI/ARIA/apg/patterns/switch/</a>. This site is really useful for telling you how custom components should behave and what things you need to make sure to include. And it has examples.</p>
<p>I went with the checkbox example, since the one I had to create had an on and an off state. In my case, the design had labels on either side of the toggle and the on one would show when the toggle was on and the off one when the toggle was off.</p>
<h2 id="heading-html">HTML</h2>
<p>My HTML ended up like this:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"sr-only"</span>&gt;</span>What this toggle is for<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">aria-hidden</span>=<span class="hljs-string">"true"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"off"</span>&gt;</span>Off<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"checkbox"</span> <span class="hljs-attr">role</span>=<span class="hljs-string">"switch"</span> <span class="hljs-attr">checked</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"checkbox"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"toggle"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"toggle-button"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">aria-hidden</span>=<span class="hljs-string">"true"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"on"</span>&gt;</span>On<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
</code></pre>
<p>All of the content is inside the label, so the whole thing is clickable. As with any input we need a label. The design doesn’t include one, so we need to include a span with the label and hide it from sighted users.</p>
<p>The checkbox is doing the actual work and the <code>.toggle</code> and <code>.toggle-button</code> make up the actual toggle. In the design I had the checkbox should be on by default, hence why the HTML includes the <code>checked</code> attribute.</p>
<p>Then there’s the on/off texts, which are hidden from screen readers. This is because the screen reader will announce whether the checkbox is on or off. The toggle is really only an interesting way to show a checkbox to sighted users.</p>
<h2 id="heading-css">CSS</h2>
<p>For the layout, I used a grid to put the elements all next to each other and add some code to change the opacity of the on/off texts based on the checkbox’s state.</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">label</span> {
  <span class="hljs-attribute">position</span>: relative;
  <span class="hljs-attribute">display</span>: inline-grid;
  <span class="hljs-attribute">grid-template-columns</span>: <span class="hljs-built_in">repeat</span>(<span class="hljs-number">3</span>, auto);
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">gap</span>: .<span class="hljs-number">5rem</span>;
}

<span class="hljs-selector-class">.off</span> {
  <span class="hljs-attribute">opacity</span>: <span class="hljs-number">1</span>;
  <span class="hljs-attribute">transition</span>: opacity <span class="hljs-number">250ms</span> ease;
}

<span class="hljs-selector-class">.on</span> {
  <span class="hljs-attribute">opacity</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">transition</span>: opacity <span class="hljs-number">250ms</span> ease;
}

<span class="hljs-selector-class">.checkbox</span><span class="hljs-selector-pseudo">:checked</span> ~ <span class="hljs-selector-class">.on</span> {
  <span class="hljs-attribute">opacity</span>: <span class="hljs-number">1</span>;
}

<span class="hljs-selector-class">.off</span><span class="hljs-selector-pseudo">:has(</span> ~ <span class="hljs-selector-class">.checkbox</span><span class="hljs-selector-pseudo">:checked)</span> {
  <span class="hljs-attribute">opacity</span>: <span class="hljs-number">0</span>;
}
</code></pre>
<p><code>:has</code> is useful here as a previous sibling selector. Although if it didn’t exist, we could just put the off text after the checkbox in the HTML and re-order the elements. Usually that would be a terrible idea, but in this case, they’re only there for sighted users and aren’t selectable, so it’s not going to make much difference.</p>
<p>And then there’s the toggle itself. I remembered last year hearing a talk from Sara Soueidan about hiding the default checkbox when you’re styling your own. Handily she also has a blog post about it at <a target="_blank" href="https://www.sarasoueidan.com/blog/inclusively-hiding-and-styling-checkboxes-and-radio-buttons/">https://www.sarasoueidan.com/blog/inclusively-hiding-and-styling-checkboxes-and-radio-buttons/</a>.</p>
<p>The important part of this post is that on Android you can explore a page by touch and feel interactive elements. Which means we can’t hide the checkbox by making it tiny or moving it off the page.</p>
<p>What we are doing instead is making the checkbox and toggle the same size and putting the toggle on top of the checkbox (and also adding some styling to make it look pretty).</p>
<pre><code class="lang-css"><span class="hljs-selector-pseudo">:root</span> {
  <span class="hljs-attribute">--toggle-width</span>: <span class="hljs-number">52px</span>;
  <span class="hljs-attribute">--toggle-height</span>: <span class="hljs-number">30px</span>;
  <span class="hljs-attribute">--toggle-button-width</span>: <span class="hljs-number">26px</span>;
}

<span class="hljs-selector-class">.checkbox</span> {
  <span class="hljs-attribute">position</span>: absolute;
  <span class="hljs-attribute">width</span>: <span class="hljs-built_in">var</span>(--toggle-width);
  <span class="hljs-attribute">height</span>: <span class="hljs-built_in">var</span>(--toggle-height);
  <span class="hljs-attribute">opacity</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">grid-column-start</span>: <span class="hljs-number">2</span>;
}

<span class="hljs-selector-class">.toggle</span> {
  <span class="hljs-attribute">display</span>: block;
  <span class="hljs-attribute">position</span>: relative;
  <span class="hljs-attribute">width</span>: <span class="hljs-built_in">var</span>(--toggle-width);
  <span class="hljs-attribute">height</span>: <span class="hljs-built_in">var</span>(--toggle-height);
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">30px</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.125rem</span>;
  <span class="hljs-attribute">background-color</span>: lightgrey;
  <span class="hljs-attribute">cursor</span>: pointer;  
}

<span class="hljs-selector-class">.toggle-button</span> {
  <span class="hljs-attribute">display</span>: block;
  <span class="hljs-attribute">position</span>: relative;
  <span class="hljs-attribute">top</span>: <span class="hljs-number">2px</span>;
  <span class="hljs-attribute">left</span>: <span class="hljs-number">2px</span>;
  <span class="hljs-attribute">width</span>: <span class="hljs-built_in">var</span>(--toggle-button-width);
  <span class="hljs-attribute">aspect-ratio</span>: <span class="hljs-number">1</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">50%</span>;
  <span class="hljs-attribute">background</span>: white;
  <span class="hljs-attribute">transition</span>: left <span class="hljs-number">250ms</span> ease;
}

<span class="hljs-selector-class">.checkbox</span><span class="hljs-selector-pseudo">:focus-visible</span> + <span class="hljs-selector-class">.toggle</span> {
  <span class="hljs-attribute">outline</span>: <span class="hljs-number">2px</span> solid black;
  <span class="hljs-attribute">outline-offset</span>: <span class="hljs-number">2px</span>;
}
</code></pre>
<p>Using custom properties makes sure that the checkbox and toggle are the same size. And because this is in a grid we can make sure that the absolutely positioned checkbox is in the same grid column as the checkbox.</p>
<p>Although the checkbox is turned on by default, the CSS defaults to the checkbox being turned off. All we have left is to move the toggle button over the right and change the toggle’s colour when the checkbox is checked:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.checkbox</span><span class="hljs-selector-pseudo">:checked</span> + <span class="hljs-selector-class">.toggle</span> {
  <span class="hljs-attribute">background-color</span>: red;
}

<span class="hljs-selector-class">.checkbox</span><span class="hljs-selector-pseudo">:checked</span> + <span class="hljs-selector-class">.toggle</span> &gt; <span class="hljs-selector-class">.toggle-button</span> {
  <span class="hljs-attribute">left</span>: <span class="hljs-built_in">calc</span>(var(--toggle-button-width) - <span class="hljs-number">2px</span>);
}
</code></pre>
<h2 id="heading-final-result">Final result</h2>
<p>And it ends up looking like a standard toggle:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codepen.io/nicm42/pen/NPWKgqm">https://codepen.io/nicm42/pen/NPWKgqm</a></div>
]]></content:encoded></item><item><title><![CDATA[Review of 2024]]></title><description><![CDATA[2024 had a lot going on. Just not so much in anything I blogged about or side projects I created.
I spent January looking for jobs. There was a surge in the number posted in comparison to December, and I had to do take-home tests, which took up time....]]></description><link>https://blog.nicm42.me.uk/review-of-2024</link><guid isPermaLink="true">https://blog.nicm42.me.uk/review-of-2024</guid><category><![CDATA[Blogging]]></category><dc:creator><![CDATA[Nic]]></dc:creator><pubDate>Tue, 31 Dec 2024 16:59:19 GMT</pubDate><content:encoded><![CDATA[<p>2024 had a lot going on. Just not so much in anything I blogged about or side projects I created.</p>
<p>I spent January looking for jobs. There was a surge in the number posted in comparison to December, and I had to do take-home tests, which took up time.</p>
<p>In March I started a new job and in June I got made redundant from it. It’s a familiar tale, but it’s reading other people’s stories about getting made redundant that make me realise just how lucky I got to be starting a new job a month later.</p>
<p>It was one of the few jobs I heard back from, and the only one who offered me an interview. Fortunately they offered me the job and it was one that I wanted.</p>
<p>What it’s meant that any public activity has felt like it’s all about getting a job and not about me getting to have fun. Which is annoying because there’s a side project I’d really like to do that would actually be useful to me. I’m hoping to do it in 2025!</p>
<p>In a year’s time I’d also like to still be employed with the same company, but I don’t have 100% control over that (and if someone offered me a 6 figure salary to play with bunnies/kittens/read books all day I wouldn’t say no).</p>
]]></content:encoded></item><item><title><![CDATA[Button type button]]></title><description><![CDATA[I’ve seen <button type=”button”> before but thought that you never need it. Turns out you do…
If you have a button on the page then by default its type is button.
But if that button is inside a form then by default its type is submit.
It makes sense....]]></description><link>https://blog.nicm42.me.uk/button-type-button</link><guid isPermaLink="true">https://blog.nicm42.me.uk/button-type-button</guid><category><![CDATA[HTML]]></category><dc:creator><![CDATA[Nic]]></dc:creator><pubDate>Sun, 24 Nov 2024 08:57:02 GMT</pubDate><content:encoded><![CDATA[<p>I’ve seen <code>&lt;button type=”button”&gt;</code> before but thought that you never need it. Turns out you do…</p>
<p>If you have a button on the page then by default its type is button.</p>
<p>But if that button is inside a form then by default its type is submit.</p>
<p>It makes sense. A button outside of a form with a type of submit wouldn’t make sense: what would it submit? Same with reset. So it makes sense that the only thing the button can do is to be a button. But if you have a form the most likely thing that button will do is to submit the form (the second most likely is to reset the form).</p>
<p>So if your form contains a button that doesn’t submit or reset the form (as I had recently), it needs to be of type button.</p>
]]></content:encoded></item><item><title><![CDATA[CSS is important (or should that be !important?)]]></title><description><![CDATA[It can be depressing sometimes on the internet reading full stack and backend developers thinking that CSS is easy. Anyone can just bung something in and it’ll be fine. As a front end developer it makes me wonder what the point of me is.
The thing is...]]></description><link>https://blog.nicm42.me.uk/css-is-important-or-should-that-be-important</link><guid isPermaLink="true">https://blog.nicm42.me.uk/css-is-important-or-should-that-be-important</guid><category><![CDATA[Frontend Development]]></category><dc:creator><![CDATA[Nic]]></dc:creator><pubDate>Sun, 10 Nov 2024 17:52:27 GMT</pubDate><content:encoded><![CDATA[<p>It can be depressing sometimes on the internet reading full stack and backend developers thinking that CSS is easy. Anyone can just bung something in and it’ll be fine. As a front end developer it makes me wonder what the point of me is.</p>
<p>The thing is, CSS isn’t easy. You can’t just bung something in and hope for the best. I’ve seen that this year in CMS plugins that you pay for that have terrible CSS.</p>
<p>There are a lot of floats going on, which feels outdated. Who uses floats for layouts these days? There also doesn’t feel like a lot of testing going on. It might look ok, but then fail when you resize the browser. They’ve written it assuming that the size of the browser on load is the size it will always be. But on a mobile or tablet you might turn it round. On a computer you might maximise your browser window - or make it smaller.</p>
<p>That’s the thing with working on the front end: users could do anything. It’s our job to anticipate that and test for it. Front end developers are useful after all!</p>
]]></content:encoded></item><item><title><![CDATA[A formative experience]]></title><description><![CDATA[I was talking to a friend recently about going to university and discovering the internet properly for the first time. We both used it do something we now do a lot.
The Windows computers had the first four versions of Netscape Navigator on them (the ...]]></description><link>https://blog.nicm42.me.uk/a-formative-experience</link><guid isPermaLink="true">https://blog.nicm42.me.uk/a-formative-experience</guid><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Nic]]></dc:creator><pubDate>Mon, 23 Sep 2024 17:31:24 GMT</pubDate><content:encoded><![CDATA[<p>I was talking to a friend recently about going to university and discovering the internet properly for the first time. We both used it do something we now do a lot.</p>
<p>The Windows computers had the first four versions of Netscape Navigator on them (the big browser at the time). I used them to see what websites worked in each of them. From what I remember, most things worked in the last three and hardly anything worked in the first one. Frames certainly didn’t and that was the big trend of the time.</p>
<p>At the time I had no idea it was the sort of thing you could get a job in. And now I get paid to do that sort of thing.</p>
]]></content:encoded></item><item><title><![CDATA[Flowtime (ish)]]></title><description><![CDATA[I've been using the Pomodoro technique for a while. I started during lockdown when I noticed that working from home meant I was distracted less during the day when working from the office. The consequence was that I would be too tired to think halfwa...]]></description><link>https://blog.nicm42.me.uk/flowtime-ish</link><guid isPermaLink="true">https://blog.nicm42.me.uk/flowtime-ish</guid><category><![CDATA[Productivity]]></category><dc:creator><![CDATA[Nic]]></dc:creator><pubDate>Sun, 25 Aug 2024 11:04:41 GMT</pubDate><content:encoded><![CDATA[<p>I've been using the Pomodoro technique for a while. I started during lockdown when I noticed that working from home meant I was distracted less during the day when working from the office. The consequence was that I would be too tired to think halfway through the afternoon.</p>
<p>It still works - on the occasion that I ignore the breaks or don't turn it on I regret it. Although I only work 25 minutes in every 30 minutes, I get more done. And it's really useful if I'm doing a task that's boring or I don't like as I can tell myself I only have to do it for 25 minutes.</p>
<p>However, that 25 minutes is sometimes too short. And although 5 minutes is short enough that I can pick back up where I left off, it's not long enough to do anything much apart from wait for the end of the break. Although I've sometimes used them to do boring tasks around the house, like cleaning, where it then becomes 'how much can I do in 5 minutes', thus making the task more interesting.</p>
<p>I tried a 52/17 and found the 52 minutes of working was better, but the 17 minutes was then too long and I scratching about for things to do during that break.</p>
<p>So I looked around for alternatives and discovered the Flowtime technique. The basic idea (or at least what I took from it) is that you work until you come to a natural pause and then you take a break. The length of your break depends on how long you've been working since the last break and how long you need to feel refreshed.</p>
<p>The Pomodoro technique has many apps and websites, but because Flowtime is more wishy-washy, it doesn't have any. Instead I use a tracker designed for tracking how long you work, so I can see how long it's been since my last break. And towards the end of the day I can see how many time I've actually done, so I can work out when to finish.</p>
<p>And it's working out well. It helps that I work from home, so it's relatively easy to find something to do during the break. Or nothing if that's what I need to do to recharge. I don't end up ignoring my Pomodoro breaks because I just need to finish something I'm in the middle of that will take two minutes. And it feels natural to have a pause after I've finished a task and before I start the next one.</p>
]]></content:encoded></item><item><title><![CDATA[An A to Z of CSS]]></title><description><![CDATA[This is inspired by me walking into a room to find my parents and nephew trying to do an A to Z of cars.
This is inspired by me walking into a room to find my parents and nephew trying to do an A to Z of cars.
A accessibility
What a lot of people for...]]></description><link>https://blog.nicm42.me.uk/an-a-to-z-of-css</link><guid isPermaLink="true">https://blog.nicm42.me.uk/an-a-to-z-of-css</guid><category><![CDATA[CSS]]></category><dc:creator><![CDATA[Nic]]></dc:creator><pubDate>Sun, 04 Aug 2024 19:58:01 GMT</pubDate><content:encoded><![CDATA[<p>This is inspired by me walking into a room to find my parents and nephew trying to do an A to Z of cars.</p>
<p>This is inspired by me walking into a room to find my parents and nephew trying to do an A to Z of cars.</p>
<h2 id="heading-a-accessibility">A <code>accessibility</code></h2>
<p>What a lot of people forget, but I think is just as important as responsiveness.</p>
<h2 id="heading-b-block">B <code>block</code></h2>
<p>Perpendicular to the writing direction.</p>
<h2 id="heading-c-calc">C <code>calc</code></h2>
<p>Doing calculations in CSS. Very handy.</p>
<h2 id="heading-d-display">D <code>display</code></h2>
<p>block, inline, inline-block, flex, inline-flex, grid, inline-grid, none, etc</p>
<h2 id="heading-e-empty">E <code>:empty</code></h2>
<p>A useful pseudo-class for styling elements with no children. As long as they don't have any white space within them.</p>
<h2 id="heading-f-flexbox">F <code>flexbox</code></h2>
<p>When I first started CSS I learnt by looking at the Inspector and playing with things. And then I copied someone who did their layouts with floats. And it was so hard to position things. Learning about flexbox made all the difference.</p>
<h2 id="heading-g-grid">G <code>grid</code></h2>
<p>What still feels to me like flexbox's more complicated and more powerful sibling.</p>
<h2 id="heading-h-height">H <code>height</code></h2>
<p>How much space something takes up horizontally.</p>
<h2 id="heading-i-inline">I <code>inline</code></h2>
<p>The writing direction. Really useful for centering, where you can use margin-inline: auto.</p>
<h2 id="heading-j-justify">J <code>justify</code></h2>
<p>In <a target="_blank" href="https://mastery.games/post/flexboxzombies2/">Flexbox Zombies</a>, you use your justify laser to target zombies in the direction you're firing.</p>
<h2 id="heading-k-keyframes">K <code>@keyframes</code></h2>
<p>Animation steps.</p>
<h2 id="heading-l-line-height">L <code>line-height</code></h2>
<p>Can make text look totally unreadable if it's too big or too small.</p>
<h2 id="heading-m-margin">M <code>margin</code></h2>
<p>Space around an element that doesn't make the element bigger.</p>
<h2 id="heading-n-none">N <code>none</code></h2>
<p>Useful to stop displaying something or removing borders.</p>
<h2 id="heading-o-object-fit">O <code>object-fit</code></h2>
<p>Although it seems like this has been around forever, I came across something a while back that was relatively. It included some JavaScript someone had written to cope with object-fit being new and not working in all browsers yet.</p>
<h2 id="heading-p-padding">P <code>padding</code></h2>
<p>Spacing around the element that makes the element bigger. Negative padding is not a thing like negative margin is.</p>
<h2 id="heading-q-queries">Q <code>queries</code></h2>
<p>Media queries and container queries, used for responsive design and accessibility.</p>
<h2 id="heading-r-responsiveness">R <code>responsiveness</code></h2>
<p>Important for making sure your site fits on all screens. Seems pretty basic, but there are plenty of sites which don't fit properly at some sizes.</p>
<h2 id="heading-s-specificity">S <code>specificity</code></h2>
<p>This becomes a pain when trying to override styles from a third party. Although recently I have seen some CMS plugins using :where, which helps a lot.</p>
<h2 id="heading-t-text">T <code>text</code></h2>
<p>Sometimes used for font styling. And sometimes you use font. Except if you want to change the colour of the text, in which case it's neither.</p>
<h2 id="heading-u-units">U <code>units</code></h2>
<p>The most used of these are %, px, em and rem. Also fr in grids and s and ms in transitions and animations.</p>
<h2 id="heading-v-visibility">V <code>visibility</code></h2>
<p>Interesting, the opposite of visible is hidden. Which is not what you'd logically think it would be.</p>
<h2 id="heading-w-width">W <code>width</code></h2>
<p>How much space something takes up horizontally.</p>
<h2 id="heading-x-overflow-x">X <code>overflow-x</code></h2>
<p>A bit of a cheat for x, but there's nothing beginning with x. This controls the horizontal overflow. x is also used with Tailwind where eg <code>mx</code> is the horizontal margin.</p>
<h2 id="heading-y-overflow-y">Y <code>overflow-y</code></h2>
<p>As with x, this controls the vertical overflow. And y is generally to denote vertical in Tailwind.</p>
<h2 id="heading-z-z-index">Z <code>z-index</code></h2>
<p>The highest/lowest z-index you can use is (-)2,147,483,647. Or infinity will give you the same thing. But typing a load of 9s and then wondering why it isn't working is a much simpler method.</p>
]]></content:encoded></item><item><title><![CDATA[aria-live]]></title><description><![CDATA[I recently created a small project where it would have a page containing text, an image and a button. When you click the button the content changes to different text, image and button. In reality, it was just hiding and showing different sections of ...]]></description><link>https://blog.nicm42.me.uk/aria-live</link><guid isPermaLink="true">https://blog.nicm42.me.uk/aria-live</guid><category><![CDATA[Accessibility]]></category><dc:creator><![CDATA[Nic]]></dc:creator><pubDate>Sun, 21 Jul 2024 12:53:24 GMT</pubDate><content:encoded><![CDATA[<p>I recently created a small project where it would have a page containing text, an image and a button. When you click the button the content changes to different text, image and button. In reality, it was just hiding and showing different sections of the HTML. But the problem with it was that once you click the button the screen reader says nothing at all.</p>
<h1 id="heading-aria-live-to-the-rescue">aria-live to the rescue!</h1>
<p>I hadn't used <code>aria-live</code>, but I knew it was used to read out something on the page that had been added. Its default state is <code>off</code>, then there's <code>polite</code> and <code>assertive</code>. <code>polite</code> means it'll finish whatever it's reading, then read out the next section. <code>assertive</code> will stop whatever it's doing and read it straightaway.</p>
<p>In my project, once you click the button it takes some time to fade the current section out and fade in the new one in. And the button doesn't take that long to read out, so in this project, <code>polite</code> and <code>assertive</code> effectively do the same thing. Although I used <code>polite</code> since that makes the most sense.</p>
<p>And then I found the trouble. It read the text, skipped the image and read the text on the button. Not necessarily in that order, even though that's the order they are on the page.</p>
<p>The thing with <code>aria-live</code> is that this isn't usually used to read out the whole contents of a page. It read it out in the order it received it, which depends on the browser and the computer and what else you might be doing.</p>
<p>It also is a problem that it doesn't tell you a button is a button.</p>
<p>So it turned out that <code>aria-live</code> was not the solution I thought it was.</p>
<h2 id="heading-js-to-the-rescue">JS to the rescue</h2>
<p>But what you can do is add focus to the new section. That's easy to do in JS with <code>section.focus()</code>. Then it just starts reading from where the focus is. You can put the focus on anything, it doesn't need to be something focusable with the keyboard.</p>
<p>The downside to this approach is if you've used the keyboard to click the button, the new section will have the browser's default outline. Since it's no use on this occasion, it's fine to just remove the outline from the section (but not from everything!).</p>
]]></content:encoded></item><item><title><![CDATA[Spotlight on hidden background]]></title><description><![CDATA[The logical way of thinking about this is to have a background (colour, gradient or image) with an overlay that hides it. The spotlight then makes that part of the overlay transparent. But that's all we actually want to make it look like we're doing....]]></description><link>https://blog.nicm42.me.uk/spotlight-on-hidden-background</link><guid isPermaLink="true">https://blog.nicm42.me.uk/spotlight-on-hidden-background</guid><category><![CDATA[CSS]]></category><category><![CDATA[js]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Nic]]></dc:creator><pubDate>Sun, 07 Jul 2024 16:05:12 GMT</pubDate><content:encoded><![CDATA[<p>The logical way of thinking about this is to have a background (colour, gradient or image) with an overlay that hides it. The spotlight then makes that part of the overlay transparent. But that's all we actually want to make it look like we're doing. Instead, we can have the background on top and hide most of it. Here's how to do it.</p>
<h2 id="heading-initial-set-up">Initial set up</h2>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"spotlight"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<pre><code class="lang-css"><span class="hljs-selector-tag">html</span>,
<span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">height</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
}

<span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">position</span>: relative;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">background-color</span>: black;
}
</code></pre>
<p>This gives us a black background that fills the screen. The <code>.spotlight</code> div will be a circle that reveals the background.</p>
<h2 id="heading-the-spotlight">The spotlight</h2>
<pre><code class="lang-css"><span class="hljs-selector-class">.spotlight</span> {
  <span class="hljs-attribute">position</span>: absolute;
  <span class="hljs-attribute">inset</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">background-image</span>: <span class="hljs-built_in">linear-gradient</span>(to bottom right, 
    red, orange, yellow, green, blue, indigo, violet,
    red, orange, yellow, green, blue, indigo, violet);
  <span class="hljs-attribute">clip-path</span>: <span class="hljs-built_in">circle</span>(<span class="hljs-number">5%</span> at <span class="hljs-number">50%</span> <span class="hljs-number">50%</span>);
}
</code></pre>
<p>I've positioned the spotlight absolutely so we can move it with the mouse. It fills the screen and I've added a rainbow background gradient, since it's June and Pride month. It's not one where every colour perfectly blends into another, and there's some pink between the violet and red. But since we'll only get to see a little of it at a time, it doesn't matter.</p>
<p>The <code>clip-path</code> creates a small circle in the middle of the screen. Anything outside of it is hidden, so we only see the background inside that small circle.</p>
<h2 id="heading-moving-the-spotlight">Moving the spotlight</h2>
<p>To move the spotlight, all we need to do is to update the clip-path circle position. The easiest way to do that is to use custom properties:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.spotlight</span> {
  <span class="hljs-attribute">--left-circle</span>: <span class="hljs-number">50%</span>;
  <span class="hljs-attribute">--top-circle</span>: <span class="hljs-number">50%</span>;
  <span class="hljs-attribute">position</span>: absolute;
  <span class="hljs-attribute">inset</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">background-image</span>: <span class="hljs-built_in">linear-gradient</span>(to bottom right, 
    red, orange, yellow, green, blue, indigo, violet,
    red, orange, yellow, green, blue, indigo, violet);
  <span class="hljs-attribute">clip-path</span>: <span class="hljs-built_in">circle</span>(<span class="hljs-number">5%</span> at var(--left-circle) <span class="hljs-built_in">var</span>(--top-circle));
}
</code></pre>
<p>Then we can use the mousemove event listener to detect where the mouse is:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> spotlight = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'.spotlight'</span>);

spotlight.addEventListener(<span class="hljs-string">'mousemove'</span>, <span class="hljs-function"><span class="hljs-params">e</span> =&gt;</span> {
  spotlight.style.setProperty(<span class="hljs-string">'--left-circle'</span>, e.clientX + <span class="hljs-string">'px'</span>);
  spotlight.style.setProperty(<span class="hljs-string">'--top-circle'</span>, e.clientY + <span class="hljs-string">'px'</span>);
});
</code></pre>
<p>clientX and clientY will get us the horizontal and vertical mouse position, where (0, 0) is the top left. This means that if you move the mouse to the very top left of the screen the clip-path becomes <code>clip-path: circle(5% at 0 0);</code></p>
<p>All you have to do is to move your mouse around you can see different parts of the background in the circle.</p>
<h2 id="heading-adding-some-blur">Adding some blur</h2>
<p>The edge of the circle being less sharp would make it look better. A little more like a spotlight, which has a lot of light in the centre and less at the edges. However, adding blur to the edge is a little complicated when you have a clip-path.</p>
<p>If you didn't have a <code>clip-path</code>, then a <code>box-shadow</code> would do it. However, our <code>.spotlight</code> fills the screen, so we won't be able to see a shadow. A <code>drop-shadow</code> doesn't help because it has the same problem.</p>
<p>The solution is to add a <code>drop-shadow</code> to the element, but then have another inside it with the <code>clip-path</code> and background:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.spotlight</span> {
  <span class="hljs-attribute">--left-circle</span>: <span class="hljs-number">50%</span>;
  <span class="hljs-attribute">--top-circle</span>: <span class="hljs-number">50%</span>;
  <span class="hljs-attribute">position</span>: absolute;
  <span class="hljs-attribute">inset</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">filter</span>: <span class="hljs-built_in">drop-shadow</span>(<span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">20px</span> rgba(<span class="hljs-number">255</span>, <span class="hljs-number">255</span>, <span class="hljs-number">255</span>, .<span class="hljs-number">75</span>));

  &amp;::before {
    <span class="hljs-attribute">content</span>: <span class="hljs-string">''</span>;
    <span class="hljs-attribute">position</span>: absolute;
    <span class="hljs-attribute">inset</span>: <span class="hljs-number">0</span>;
    <span class="hljs-attribute">background-image</span>: <span class="hljs-built_in">linear-gradient</span>(to bottom right, red, orange, yellow, green, blue, indigo, violet, red, orange, yellow, green, blue, indigo, violet);
    <span class="hljs-attribute">clip-path</span>: <span class="hljs-built_in">circle</span>(<span class="hljs-number">5%</span> at var(--left-circle) <span class="hljs-built_in">var</span>(--top-circle));
  }
}
</code></pre>
<p>And there you have a white blur around the circle.</p>
<h2 id="heading-final-code">Final code</h2>
<p>Here is the final code:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codepen.io/nicm42/pen/vYwdapX">https://codepen.io/nicm42/pen/vYwdapX</a></div>
]]></content:encoded></item><item><title><![CDATA[Converting px in SCSS]]></title><description><![CDATA[A lot of the time in designs they give the font-size, line-height and letter-spacing in px. Which isn't helpful as they mean having to get the calculator out to convert them to more useful units. But a combination of Sass functions and mixins can do ...]]></description><link>https://blog.nicm42.me.uk/converting-px-in-scss</link><guid isPermaLink="true">https://blog.nicm42.me.uk/converting-px-in-scss</guid><category><![CDATA[scss]]></category><dc:creator><![CDATA[Nic]]></dc:creator><pubDate>Sun, 23 Jun 2024 12:47:17 GMT</pubDate><content:encoded><![CDATA[<p>A lot of the time in designs they give the font-size, line-height and letter-spacing in px. Which isn't helpful as they mean having to get the calculator out to convert them to more useful units. But a combination of Sass functions and mixins can do the calculations for us.</p>
<h2 id="heading-converting-font-size">Converting font-size</h2>
<p>Here we want to use rems. This function is from <a target="_blank" href="https://dev.to/rekomat/comment/1ib3b">https://dev.to/rekomat/comment/1ib3b</a>:</p>
<pre><code class="lang-scss"><span class="hljs-comment">// From https://dev.to/rekomat/comment/1ib3b</span>
<span class="hljs-keyword">@function</span> pxToRem(<span class="hljs-variable">$pxValue</span>) {
  <span class="hljs-keyword">@return</span> math.div(<span class="hljs-variable">$pxValue</span>, <span class="hljs-number">16px</span>) * <span class="hljs-number">1rem</span>;
}
</code></pre>
<p>This is literally doing the same calculation as we do on the calculator: font-size divided by 16px and then written in rems. If your base font size has been set to 10px (for example) then you'd need to change the 16px in the function to be 10px.</p>
<p>You can use this function to convert anything from px to rem, not just font-sizes.</p>
<h2 id="heading-converting-line-height">Converting line-height</h2>
<p>Here we do exactly the same as on the calculator to get a ratio:</p>
<pre><code class="lang-scss"><span class="hljs-keyword">@function</span> convertLineHeight(<span class="hljs-variable">$fontSize</span>, <span class="hljs-variable">$lineHeight</span>) {
  <span class="hljs-keyword">@return</span> math.div(<span class="hljs-variable">$lineHeight</span>, <span class="hljs-variable">$fontSize</span>);
}
</code></pre>
<h2 id="heading-converting-letter-spacing">Converting letter-spacing</h2>
<p>And again it's the same calculator calculation and expressed in ems:</p>
<pre><code class="lang-scss"><span class="hljs-keyword">@function</span> convertLetterSpacing(<span class="hljs-variable">$fontSize</span>, <span class="hljs-variable">$letterSpacing</span>) {
  <span class="hljs-keyword">@return</span> math.div(<span class="hljs-variable">$letterSpacing</span>, <span class="hljs-variable">$fontSize</span>) * <span class="hljs-number">1em</span>;
}
</code></pre>
<h2 id="heading-using-the-functions">Using the functions</h2>
<p>To use those three functions we'd do this:</p>
<pre><code class="lang-scss"><span class="hljs-selector-tag">p</span> {
  <span class="hljs-attribute">font-size</span>: pxToRem(<span class="hljs-number">24px</span>);
  <span class="hljs-attribute">line-height</span>: convertLineHeight(<span class="hljs-number">24px</span>, <span class="hljs-number">30px</span>);
  <span class="hljs-attribute">letter-spacing</span>: convertLetterSpacing(<span class="hljs-number">24px</span>, -<span class="hljs-number">0.48px</span>);
}
</code></pre>
<p>Which is fine, but we have to give all three of them the font-size. We can use a mixin to make this more efficient:</p>
<pre><code class="lang-scss"><span class="hljs-keyword">@mixin</span> textUtils(<span class="hljs-variable">$fontSize</span>, <span class="hljs-variable">$lineHeight</span>: <span class="hljs-number">1</span>, <span class="hljs-variable">$letterSpacing</span>: <span class="hljs-number">0</span>) {
  <span class="hljs-attribute">font-size</span>: pxToRem(<span class="hljs-variable">$fontSize</span>);
  <span class="hljs-keyword">@if</span> <span class="hljs-variable">$lineHeight</span> != <span class="hljs-number">1</span> {
    <span class="hljs-attribute">line-height</span>: convertLineHeight(<span class="hljs-variable">$fontSize</span>, <span class="hljs-variable">$lineHeight</span>);
  }
  <span class="hljs-keyword">@if</span> <span class="hljs-variable">$letterSpacing</span> != <span class="hljs-number">0</span> {
    <span class="hljs-attribute">letter-spacing</span>: convertLetterSpacing(<span class="hljs-variable">$fontSize</span>, <span class="hljs-variable">$letterSpacing</span>);
  }
}

<span class="hljs-selector-tag">p</span> {
  <span class="hljs-keyword">@include</span> textUtils(<span class="hljs-number">24px</span>, <span class="hljs-number">30px</span>, -<span class="hljs-number">0.48px</span>);
  <span class="hljs-comment">//@include textUtils(24px, 30px);</span>
  <span class="hljs-comment">//@include textUtils(24px);</span>
}
</code></pre>
<p>This mixin is set up so it needs the font-size, but it doesn't matter if you don't give it the letter-spacing, because that could be 0. And just in case, it also works if you don't give it the line-height.</p>
]]></content:encoded></item><item><title><![CDATA[Accessibility on TV]]></title><description><![CDATA[There are all these rules for accessibility on websites, which admittedly most sites don't follow, but theoretically they should. However that's not the case on TV.
Last week's Doctor Who Dot and Bubble was a case in point. The main character spends ...]]></description><link>https://blog.nicm42.me.uk/accessibility-on-tv</link><guid isPermaLink="true">https://blog.nicm42.me.uk/accessibility-on-tv</guid><category><![CDATA[Accessibility]]></category><dc:creator><![CDATA[Nic]]></dc:creator><pubDate>Sun, 09 Jun 2024 12:39:32 GMT</pubDate><content:encoded><![CDATA[<p>There are all these rules for accessibility on websites, which admittedly most sites don't follow, but theoretically they should. However that's not the case on TV.</p>
<p>Last week's Doctor Who <em>Dot and Bubble</em> was a case in point. The main character spends her time in a literal social media bubble. It goes around her head so she can't see the real world. In the background are rectangles with various people in. In the foreground are rectangles of whoever she's talking to.</p>
<p><img src="https://www.doctorwhotv.co.uk/wp-content/uploads/DW-14.5-D-dot-and-bubble.jpg" alt="The bubble in question" class="image--center mx-auto" /></p>
<p>That all sounds fine right? Now what if I tell you the background images constantly move around the bubble. From the perspective of the person looking at them, the background moves from one side of your vision to the other. Constantly. At a constant speed.</p>
<p>If you have a vestibular problem that is probably the worst Doctor Who episode to attempt to watch. Every time we saw that background moving I was distracted thinking about people who had to give up on the episode because it made them too ill.</p>
<p>And if that existed in real life, surely the constantly moving background would make you feel travel sick? The minute you turn it off you wouldn't be able to walk in a straight line. And if you tried standing up you'd end up leaning to one side.</p>
<p>There are warnings on TV before episodes, like for flashing images for example. But no one warns for a lot of motion. They should, though. Accessibility should apply to TV. I'd like to be able to turn off motion on my TV and watch the version where the background rectangles stay where they are. Then I could concentrate on the episode itself. And maybe I'd have enjoyed it more.</p>
]]></content:encoded></item><item><title><![CDATA[Animated border]]></title><description><![CDATA[Something which I've done recently is an animated border. It starts off as no border at all, then you see it drawing the border from one corner. Of course there's no simple way to animate the whole border.
It sounds complicated, the code is a bit com...]]></description><link>https://blog.nicm42.me.uk/animated-border</link><guid isPermaLink="true">https://blog.nicm42.me.uk/animated-border</guid><category><![CDATA[CSS]]></category><dc:creator><![CDATA[Nic]]></dc:creator><pubDate>Sun, 19 May 2024 16:56:56 GMT</pubDate><content:encoded><![CDATA[<p>Something which I've done recently is an animated border. It starts off as no border at all, then you see it drawing the border from one corner. Of course there's no simple way to animate the whole border.</p>
<p>It sounds complicated, the code is a bit complicated, but it's not once you understand how it's doing it.</p>
<h2 id="heading-the-setup">The setup</h2>
<p>All you need is an element that you're drawing the border around, and a way to start the border drawing. I've drawn the border around an image and a paragraph in the past, but for this demo I'm doing it around a div. I've added a background colour to the div and some padding on it so the border isn't touching it. I've added the border when you scroll to it in the past, but for this demo it adds the border when you click the button.</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span>Add border<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<pre><code class="lang-css"><span class="hljs-selector-pseudo">:root</span> {
  <span class="hljs-attribute">--border-colour</span>: black;  
  <span class="hljs-attribute">--border-thickness</span>: <span class="hljs-number">5px</span>;
  <span class="hljs-attribute">--padding</span>: <span class="hljs-number">8px</span>;
  <span class="hljs-attribute">--border-transition-time</span>: <span class="hljs-number">0ms</span>;
}

<span class="hljs-keyword">@media</span> screen <span class="hljs-keyword">and</span> (<span class="hljs-attribute">prefers-reduced-motion:</span> no-preference) { 
  <span class="hljs-selector-pseudo">:root</span> {
    <span class="hljs-attribute">--border-transition-time</span>: <span class="hljs-number">250ms</span>;
  }
}

<span class="hljs-selector-tag">div</span> {
  <span class="hljs-attribute">position</span>: relative;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">200px</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">100px</span>;
  <span class="hljs-attribute">top</span>: <span class="hljs-number">50px</span>;
  <span class="hljs-attribute">left</span>: <span class="hljs-number">50px</span>;
  <span class="hljs-attribute">background-color</span>: pink;
  <span class="hljs-attribute">padding</span>: <span class="hljs-built_in">var</span>(--padding);
}
</code></pre>
<p>Here I've added some custom properties because we're going to be using those values multiple times. And I've just moved the div away from the edge a bit so it's easier to see. The only line of the div code you need on your element is the <code>position: relative</code>, everything is is just for this demo.</p>
<p>I've also added a reduce motion media query. This way, if anyone has reduced motion turned on they won't see the border animating. Instead the whole thing will just appear.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> button = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'button'</span>);
<span class="hljs-keyword">const</span> div = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'div'</span>);

button.addEventListener(<span class="hljs-string">'click'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
  div.classList.add(<span class="hljs-string">'border'</span>);
});
</code></pre>
<p>Then there's a bit of JavaScript to add a class to the border when the button is clicked. We'll use that class to show the border.</p>
<h2 id="heading-the-border-set-up">The border set up</h2>
<p>To do this we're going to use pseudo-elements. One will be the top and right border, the other will be the bottom and left border. Our animation is going to go from the top left, across, down, across and back up to the top left.</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">div</span><span class="hljs-selector-pseudo">::before</span>,
<span class="hljs-selector-tag">div</span><span class="hljs-selector-pseudo">::after</span> {
  <span class="hljs-attribute">content</span>: <span class="hljs-string">''</span>;
  <span class="hljs-attribute">position</span>: absolute;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">z-index</span>: -<span class="hljs-number">1</span>;
}

<span class="hljs-selector-tag">div</span><span class="hljs-selector-pseudo">::before</span> {
  <span class="hljs-attribute">top</span>: <span class="hljs-built_in">calc</span>(var(--padding) * -<span class="hljs-number">1</span> - <span class="hljs-built_in">var</span>(--border-thickness));
  <span class="hljs-attribute">left</span>: <span class="hljs-built_in">calc</span>(var(--padding) * -<span class="hljs-number">1</span>);
  <span class="hljs-attribute">border-top</span>: <span class="hljs-number">0px</span> solid <span class="hljs-built_in">var</span>(--border-colour);
  <span class="hljs-attribute">border-right</span>: <span class="hljs-number">0px</span> solid <span class="hljs-built_in">var</span>(--border-colour);
}

<span class="hljs-selector-tag">div</span><span class="hljs-selector-pseudo">::after</span> {
  <span class="hljs-attribute">right</span>: <span class="hljs-built_in">calc</span>(var(--padding) * -<span class="hljs-number">1</span>);
  <span class="hljs-attribute">bottom</span>: <span class="hljs-built_in">calc</span>(var(--padding) * -<span class="hljs-number">1</span> - <span class="hljs-built_in">var</span>(--border-thickness));
  <span class="hljs-attribute">border-bottom</span>: <span class="hljs-number">0px</span> solid <span class="hljs-built_in">var</span>(--border-colour);
  <span class="hljs-attribute">border-left</span>: <span class="hljs-number">0px</span> solid <span class="hljs-built_in">var</span>(--border-colour);
}
</code></pre>
<p>There's a lot going on here, so let's go through it.</p>
<p>Both sets of borders start off with no width or height, so we can't see them. And a z-index of -1 so if you have text or anything you can interact with in there, the pseudo-elements won't be on top of it.</p>
<p>Both pseudo-elements contain a border - because otherwise they've cover the whole of the div. For now, those borders are 0px in width, as otherwise you'd see squares in the top left and bottom right corners: even though they have no width and height, the border is still visible.</p>
<p>There's also some positioning going on. Since I have padding around the div, I want the borders to be at the edge of that padding, so I have moved them accordingly. Otherwise the elements will ignore the padding and sit at the edge of the background.</p>
<h2 id="heading-adding-the-border">Adding the border</h2>
<p>When the class of <code>border</code> is added, all we have to do is to set the size and border-width of both pseudo-elements:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">div</span><span class="hljs-selector-class">.border</span><span class="hljs-selector-pseudo">::before</span>,
<span class="hljs-selector-tag">div</span><span class="hljs-selector-class">.border</span><span class="hljs-selector-pseudo">::after</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-built_in">calc</span>(<span class="hljs-number">100%</span> + var(--padding) * <span class="hljs-number">2</span>);
  <span class="hljs-attribute">height</span>: <span class="hljs-built_in">calc</span>(<span class="hljs-number">100%</span> + var(--padding) * <span class="hljs-number">2</span> + <span class="hljs-built_in">var</span>(--border-thickness));
  <span class="hljs-attribute">border-width</span>: <span class="hljs-built_in">var</span>(--border-thickness);
}
</code></pre>
<p>Of course this just adds the border instantly. We also need to transition them so we can see them animate:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">div</span><span class="hljs-selector-pseudo">::before</span> {
  <span class="hljs-comment">/* ... */</span>
  <span class="hljs-attribute">transition</span>:
    width <span class="hljs-built_in">var</span>(--border-transition-time) linear,
    height <span class="hljs-built_in">var</span>(--border-transition-time) linear <span class="hljs-built_in">var</span>(--border-transition-time);
}

<span class="hljs-selector-tag">div</span><span class="hljs-selector-pseudo">::after</span> {
  <span class="hljs-comment">/* ... */</span>
  <span class="hljs-attribute">transition</span>:
    width <span class="hljs-built_in">var</span>(--border-transition-time) linear <span class="hljs-built_in">calc</span>(var(--border-transition-time) * <span class="hljs-number">2</span>),
    height <span class="hljs-built_in">var</span>(--border-transition-time) linear <span class="hljs-built_in">calc</span>(var(--border-transition-time) * <span class="hljs-number">3</span>),
    border-width <span class="hljs-number">0s</span> linear <span class="hljs-built_in">calc</span>(var(--border-transition-time) * <span class="hljs-number">2</span>);
}
</code></pre>
<p>These are a bit lengthy, so let's go through them.</p>
<p>First we want to see the border going across the top. So we transition the width of the before pseudo-element in <code>--border-transition-time</code>.</p>
<p>Then we want to see the border going down the right side. So we transition the height of the before pseudo-element in <code>--border-transition-time</code>. Except we don't want it to start until it's finished the top border. So we add a delay of <code>--border-transition-time</code>.</p>
<p>After that we want to see the border going along the bottom. Since we want that to wait until we've done the top and right, we need to delay for twice <code>--border-transition-time</code>.</p>
<p>Finally we want the border going up the left, so now we need to wait for three times <code>--border-transition-time</code>.</p>
<p>And we also need to delay the border-width changing from 0px to <code>--border-thickness</code> otherwise as soon as the top border starts animating we see a square of the after pseudo-elements border.</p>
<p>You can always change these times and delays to make the border animation less linear (as well as changing the transition timing function). You just need to be careful to keep track of what is happening when so nothing happens sooner or later than you want it to.</p>
<h2 id="heading-the-finished-code">The finished code</h2>
<p>Here is the final code:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codepen.io/nicm42/pen/oNRbaaY">https://codepen.io/nicm42/pen/oNRbaaY</a></div>
]]></content:encoded></item><item><title><![CDATA[Earth Day Celebration Landing Page]]></title><description><![CDATA[This is a submission for Frontend Challenge v24.04.17, Glam Up My Markup: Earth Day Celebration Landing Page
What I Built
I built the Earth Day Celebration Landing Page.
I didn't have a whole lot of time to do it, so I decided to not include any imag...]]></description><link>https://blog.nicm42.me.uk/earth-day-celebration-landing-page</link><guid isPermaLink="true">https://blog.nicm42.me.uk/earth-day-celebration-landing-page</guid><category><![CDATA[challenges]]></category><category><![CDATA[frontend]]></category><dc:creator><![CDATA[Nic]]></dc:creator><pubDate>Sat, 27 Apr 2024 19:20:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1714245598561/d847df04-3b10-44b2-9bfe-9c82fd2ff481.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>This is a submission for</em> <a target="_blank" href="https://dev.to/devteam/join-us-for-the-next-frontend-challenge-earth-day-edition-52e4"><em>Frontend Challenge v24.04.17</em></a><em>, Glam Up My Markup: Earth Day Celebration Landing Page</em></p>
<h2 id="heading-what-i-built">What I Built</h2>
<p>I built the Earth Day Celebration Landing Page.</p>
<p>I didn't have a whole lot of time to do it, so I decided to not include any images (which would definitely save time searching for the perfect ones!) and only include one bit of animation.</p>
<p>I chose nature inspired colours, so mostly browns and greens with a bit of blue and yellow in there.</p>
<h2 id="heading-demo">Demo</h2>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codepen.io/nicm42/pen/oNOVObV">https://codepen.io/nicm42/pen/oNOVObV</a></div>
<p> </p>
<p>{% codepen <a target="_blank" href="https://codepen.io/nicm42/pen/oNOVObV">https://codepen.io/nicm42/pen/oNOVObV</a> %}</p>
<h2 id="heading-journey">Journey</h2>
<p>I had been thinking recently about an animation I did that drew a border around something on scroll. But I did it at my previous work, and no longer have access to what I did. So I wanted to reproduce it to remember how to do it, and to have a record of it in the future if I want to do it, or something like it, again.</p>
<p>That took a while, so beyond that I made the sections stand out a little bit, without doing too much or taking too much time. There's not a lot of classes in there, so targeting specific ones took a bit of thought.</p>
]]></content:encoded></item></channel></rss>