Jan 22, 2024
Per Djurner

How to create a blank favicon

A favicon is the small image you see in a browser tab representing a site. If you prefer a minimalist approach and want no favicon at all, there's a simple trick: use a data URI scheme. By placing <link rel="icon" href="data:,"> in the HTML's <head> section, you create a blank favicon. This line embeds an empty data string, making the browser recognize a favicon but display nothing.

For myself, this is particularly useful during development of a new site where I may not have gotten to the stage where it's time to create a proper favicon yet. Setting it to blank like this keeps me from getting distracted, wondering why there is a 404 request showing in Chrome's Developer tools.

May 10, 2023
Per Djurner

Styling child elements with Tailwind

Every now and then I wish I could style child elements from their parent instead of repeating class names over and over again. Then I remember that this is actually possible with more recent versions of Tailwind!

Here's an example using a description list element.

<dl className="[&>dt]:font-semibold [&>dd]:mb-2">
  <dt>Some term</dt>
  <dd>The description.</dd>
  <dt>Some other term</dt>
  <dd>The description for that other term.</dd>
</dl>

One thing I should note is that the more common approach for handling this is to break out the dt / dd part to a separate component in your favorite framework. That way you can have the class names on the dt / dd elements while still avoiding unnecessary repetition. For simple cases, like the above description list, it feels like overkill to refactor your code to use a separate component though.

Feb 1, 2023
Per Djurner

Search stored procedures in SQL Server

Just posting a handy query for searching through all stored procedures in MS SQL Server.

DECLARE @search NVARCHAR(100) = 'text to search for';
SELECT ROUTINE_NAME, ROUTINE_DEFINITION
FROM INFORMATION_SCHEMA.ROUTINES
WHERE ROUTINE_TYPE = 'PROCEDURE' AND ROUTINE_DEFINITION LIKE '%' + @search + '%'
Nov 26, 2022
Per Djurner

Reverting multiple Git commits

What if you've completed a feature, pushed a bunch of commits, but then you're told that the feature wasn't needed after all? That's probably a familiar situation for most developers.

Here's a simple and safe Git command that reverts multiple commits with just one commit.

git revert --no-commit HEAD~5..

This reverts the last five commits in the current branch and stages the changes for you. All you have to do after running that is to edit the commit message and make the commit.

Note that this will only work if no other commits have been made to the branch since the ones you want to revert. It also won't work if any of the commits you want to revert is a merge commit.

Nov 24, 2022
Per Djurner

Solving error 401.3 in IIS

If you get a "HTTP Error 401.3 - Unauthorized" error when running a website using IIS one possible reason for that is that you have enabled "Anonymous Authentication" but the user running it is different from the one being set for the application pool.

To fix this, click on the website in question in IIS, double-click "Authentication", select "Anonymous Authentication" and click on "Edit" in the right pane. From here you can select "Application pool identity" (instead of the default "IUSR" user) if that option is available (which it should be in IIS 10 at least) or you can manually select the same user that is used for the application pool.

Now you should be good to go.

Jun 14, 2022
Per Djurner

Using DbContext in a .NET singleton service

It is perhaps not considered best practice, but it can be done. Here's some example code for when you have registered MyService as a singleton and want to access MyDbContext from inside it.

using MyProject.Data;
using Microsoft.Extensions.DependencyInjection;

namespace MyProject.Services
{
    public class MyService
    {
        private readonly IServiceScopeFactory _scopeFactory;

        public MyService(IServiceScopeFactory scopeFactory)
        {
            _scopeFactory = scopeFactory;
        }

        public void MyMethod()
        {
            using var scope = _scopeFactory.CreateScope();
            var db = scope.ServiceProvider.GetRequiredService<MyDbCtx>();
        }
    }
}
Jan 22, 2022
Per Djurner

TypeScript and functional React components

Here's an example of how I typically add TypeScript types to a functional React component.

import { ReactNode } from "react";

type CmpProps = {
  children: ReactNode;
  classes: string;
};

export function Cmp({ children, classes }: CmpProps): JSX.Element {
  return <div className={classes}>{children}</div>;
}

I prefer this method over using the built-in React.FC type because now I can specify that the children property is required, rather than having it optional as it is in React.FC. This way, if you forget to include the children for the component, TypeScript will scream at you (or if you include children when it should not be included).

Also, note the JSX.Element return type for the function. That can be useful to have in there to catch some silly mistakes made with formatting for example. If you didn't have it specified you would still likely get a TypeScript error somewhere but it would be from where you consume the component instead, which is a little less obvious.

Jan 16, 2022
Per Djurner

Sticky footers using Tailwind

Creating a footer that sticks to the bottom of the page is fairly easy when using Tailwind (and to be fair, it's not too hard with plain old CSS either).

You can use something like the code outlined below.

<body class="flex flex-col min-h-screen">
  <header>
    Header content...
  </header>
  <main class="flex-grow pb-12">
    Main content...
  </main>
  <footer>
    Footer content...
  </footer>
</body>

That will set the body to always be at least the height of the viewport and the main element to fill the available space height wise (using Flexbox).

The bottom padding on the main element is optional, but it's probably something you want to have in order to create some space in the layout before the footer comes.

If you want the footer to actually stick to the bottom of the screen as well (i.e. always be visible regardless of how tall the main content area is), then add sticky and bottom-0 to the footer element.

Aug 13, 2021
Per Djurner

Lining up menu items horizontally

Here's the semantic HTML that's typically used to create a menu:

<nav>
  <ul>
    <li><a href="/">Menu Item 1</a></li>
    <li><a href="/">Menu Item 2</a></li>
    <li><a href="/">Menu Item 3</a></li>
  </ul>
</nav>

There are (at least) two ways to get these menu items to line up next to each other on the page (horizontally instead of vertically that is). Either you can use display: inline-block on the li elements, or you can use display: flex on the ul container element.

You'll often see the inline block approach in older code bases and the more modern flexbox approach in more recent code.

One issue with the inline block approach is that you'll have to deal with the spaces that show up between the elements, at least if you're trying to achieve a pixel perfect design. It can be solved by setting font-size: 0 on the container element (i.e. the ul) and then resetting the font size on the individual list elements.

You don't have this problem with the flexbox approach, but you do have to get rid of the browser's default list styling by setting list-style: none on the ul element.

You can check out the code in more detail on the example pages here:

Jun 23, 2021
Per Djurner

Debouncing the window resize event

Just a quick tip if you want to run some code on window resize but want to avoid freezing / slowing down the user's browser. The code below will run doSomething after the user is "done" resizing the window (in this case 500ms after the last resize event).

window.addEventListener('resize', () => {
  window.clearTimeout(window.resizeTimeout)
  window.resizeTimeout = window.setTimeout(() => {
    doSomething(this.someVariable)
  }, 500)
})
May 18, 2021
Per Djurner

Nullish coalescing operator in Vue templates

The nullish coalescing operator can be really useful when you want to display, for example, "N/A" when a value is null or undefined, but not when it's 0.

<template>
  <p>{{ val ?? 'N/A' }}</p>
</template>

<script>
export default {
  data() {
    return {
      val: 0
    }
  }
}
</script>

Note that you need Vue 3 for this to work in templates.

Jan 21, 2021
Per Djurner

Checking for nulls in C#

Checking for nulls is one of those things that leads to a lot of boilerplate code in C#. As such, I want it to take up as little space as possible so it doesn't distract me from the "real" code.

Here's my current favorite one-liner way of doing it.

_ = myArg ?? throw new ArgumentNullException(nameof(myArg));

This makes use of discards (_) and the null-coalescing operator (??).

Discards are placeholder variables that are intentionally left unused.

The null-coalescing operator returns the value on the left side if it isn't null, otherwise it returns the result of the operand on the right side.

In other words, all we're doing here is placing the value of myArg in an unused variable if it's not null, and when it is null, we throw an error instead.

You could argue that this code is a little weird and not quite as readable as using a full if statement instead, but I think that's a trade-off worth doing in this case.

By the way, there's an even better way of checking for nulls on the horizon. A feature called "Simplified Null Argument Checking" may be coming in a future release of C# (it was removed from C# 9 at a late stage). If added, all you have to do is put ! at the end of a parameter name and it will run the same code as above under the hood.

Dec 17, 2020
Per Djurner

Ignoring TypeScript files when deploying to Azure

Writing this as a note to myself mostly (as most things on this blog).

I ran into an issue where my .NET Core app could no longer be deployed to Azure. Turns out I had recently introduced some TypeScript files that were now failing in the Kudu / MSBuild step (due to differences in the version of TypeScript used on my machine versus the one used on Azure I believe). Since those TypeScript files are related to Vue and not the .NET Core app, I simply wanted the build step to ignore these files.

To do that, I added the following to my .csproj file.

<PropertyGroup>
  <TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
</PropertyGroup>
Oct 4, 2020
Per Djurner

Fade in Google font on load

It's been bothering me for a while now that when you load this blog you see a brief display of a fallback font before it gets replaced with the custom one (the Inter font family in my case). So, I decided to do something about it. Here's what I did.

First of all, I now tell the browser to not show a fallback font while downloading the custom one by setting font-display to block (technically this will render an invisible fallback font).

Since Google is supplying the CSS for the font we need to tell Google to use this setting though. This is done by passing the URL parameter display=block instead of the usual display=swap.

<link href="https://fonts.googleapis.com/css2?family=Inter&display=block" rel="stylesheet" />

Secondly, we can make the user experience a little nicer by fading in the custom font once it's been loaded by the browser. This is done by using the CSS Font Loading API.

The gist of it is that I set the main element to have an opacity of 0 to begin with. Then I use the Font Loading API to detect when the font has been loaded and start a CSS animation to set the opacity to 1. You can get the full details of this by viewing the source code of the example page.

Some notes to keep in mind if you decide to do something like this yourself:

Aug 31, 2020
Per Djurner

Emojis in .NET Core / MySQL

To support emojis when using MySQL in .NET Core (version 2.2 in my case) you need to do a few things.

First of all, add CharSet=utf8mb4; to your database connection string. In other words, your code in Startup.cs should look something like this.

string db = @"Server=domain.com;Database=db;Uid=user;Pwd=pass;CharSet=utf8mb4;";
services.AddDbContext<AppDbContext>(options => options.UseMySQL(db));

You also need to make a couple of database changes. The charset needs to be set to utf8mb4 both for the column you are storing the emojis in and as the default for the entire database.

Aug 25, 2020
Per Djurner

Highlight table row when hovering over specific table cell

Highlighting a table row when hovering over it is easy, you just use the :hover CSS pseudo-class. But what if you only want to highlight the table row when you hover over a specific table cell? It's a little more difficult because you can't achieve it using just CSS, you have to throw in some JavaScript as well.

<style>
  .highlight {
    background: #e2e8f0;
  }
</style>
<table>
  <thead>
    <tr>
      <th>Column 1</th>
      <th>Column 2</th>
      <th>Column 3</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Data 1</td>
      <td class="hoverable">Data 2</td>
      <td>Data 3</td>
    </tr>
  </tbody>
</table>
<script>
  function highlightRow(event, fn) {
    event.target.closest("tr").classList[fn]("highlight");
  }
  document.querySelectorAll(".hoverable").forEach((elm) => {
    elm.addEventListener("mouseenter", (e) => highlightRow(e, "add"));
    elm.addEventListener("mouseleave", (e) => highlightRow(e, "remove"));
  });
</script>

Open example page.

Aug 12, 2020
Per Djurner

Conditional event handler in Vue

Bit of a quick entry to write down how to conditionally add an event handler in Vue that passes a custom argument to its handler function (in addition to the event itself).

Here's the template code.

<input v-on="active ? { input: event => handle('Test', event) } : {}" />

Here's the relevant JavaScript.

data: {
  active: true,
},
methods: {
  handle(str, event) {
    console.log(str);
    console.log(event.target.value);
  }
}

Open example page.

Jul 24, 2020
Per Djurner

Better focus outline on rounded buttons

Here's a mistake I remember having made in the past. I would add some rounded corners on a button and then discover that the focus outline looked a little ugly (it would be rectangular and not follow the border radius). Then I would add outline: none to remove it, thinking I was done. Wrong!

I've now created a problem for keyboard users. If they tab to a button, they can't see that it's in focus because there are no styles to indicate that. In other words, the user interface is not as accessible as it could be.

Here's a better solution (open example page).

button {
  border: solid #d3d3d3 1px;
  border-radius: 10px;
  padding: 10px 20px;
}
button:focus {
  box-shadow: 0 0 0 1px #000;
  outline: transparent solid 1px;
}

This effectively removes the outline by making it transparent and adds a box shadow to act as the outline instead. The reason for using box-shadow is that it follows the border radius so it's no longer rectangular.

Note that we make the outline transparent instead of just removing it. This is because this way it still works in high contrast mode in Windows.

Also, if you don't like the boring 1 pixel black outline you can go for something like this instead (open example page).

button:focus {
  box-shadow: 0 0 0 3px rgba(164, 202, 254, 0.45);
}
Jul 6, 2020
Per Djurner

Clickable table rows

Turns out that making a table row clickable while keeping it accessible, and allowing there to be clickable elements inside it, is a little tricky.

Here's the code I ended up with.

<style>
  tbody tr {
    cursor: pointer;
  }
  tbody tr:hover {
    background: lightgray;
  }
</style>
<table>
  <thead>
    <tr>
      <th>Column 1</th>
      <th>Column 2</th>
      <th>Column 3</th>
    </tr>
  </thead>
  <tbody>
    <tr role="button" tabindex="0" aria-label="Show row details">
      <td>Data 1</td>
      <td><button class="skip">Button</button></td>
      <td><a href="#" class="skip"><strong>Link</strong></a></td>
    </tr>
  </tbody>
</table>
<script>
  function handleEvent(e) {
    const elements = e.composedPath();
    for (const elm of elements) {
      if (elm.tagName === "TR") {
        break;
      } else if (elm.classList.contains("skip")) {
        return;
      }
    }
    const space = 32;
    const enter = 13;
    if (e.type === "click" || e.keyCode === space || e.keyCode === enter) {
      e.preventDefault();
      console.log("Do something for table row event.");
    }
  }
  document.querySelectorAll("tbody tr").forEach((elm) => {
    elm.addEventListener("click", handleEvent);
    elm.addEventListener("keydown", handleEvent);
  });
</script>

The code is accessible because you can get to the table rows using your keyboard and select them by pressing Space or Enter.

It's possible to add links, buttons etc and have their events handled normally by adding a "skip" class to them (or on a parent element).

Jul 5, 2020
Per Djurner

Syncing a forked repository with Git

Something I run into every now and then is that I'll fork a project repository, fix a bug and submit a pull request. Some time goes by and I want to contribute something else to the project. My fork is now most likely out of date and I need to sync it with the project repository again.

In technical terms, what you need to do is configure a "remote" that points to the "upstream" repository. You can check the currently configured remote repository for your fork using this command:

git remote -v

Look for the "upstream" entries. If nothing is listed as "upstream", go ahead and configure a new remote upstream repository like this:

git remote add upstream https://github.com/OWNER/REPOSITORY.git

Once you've configured your remote you can do the following (from the master branch in your fork) every time you need to sync:

git fetch upstream (fetches code from the remote repository). git merge upstream/master (merges the changes from upstream/master into your local master branch).

Done!

Jun 25, 2020
Per Djurner

Git ignore a folder with exceptions

Usually when working with .gitignore files it's pretty straight forward, you ignore some folders and / or files and you're done. But what if you want to ignore everything inside a folder except for specific folders and files inside it?

Here's how you can do that.

folder/*
!folder/tracked-folder
!folder/tracked-file.js

As you can see the ! prefix is used here. It negates the pattern so that any matching file excluded by a previous pattern will become included again.

Jun 22, 2020
Per Djurner

Using a custom arrow in a select drop-down

Let me start by saying that you probably shouldn't do this. Stick to letting the browser render its default look for form elements as much as possible. They will be more familiar to your users that way. That said, let's say you've been told to customize these arrows a little bit, here's how you can do it.

select {
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  padding-right: 24px;
  background-image: url("data:image/svg+xml;utf8,<svg fill='%23808285' height='24' viewBox='0 0 24 24' width='24' xmlns='http://www.w3.org/2000/svg'><path d='M7 10l5 5 5-5z'/><path d='M0 0h24v24H0z' fill='none'/></svg>");
  background-repeat: no-repeat;
  background-position-x: right;
  background-position-y: 50%;
}

A few things to note...

The appearance rules will remove all browser specific styling for the select element, not just on the arrow itself. In other words, you might need to add some more CSS to get it back to looking as you want it to.

Do not use this method if you need to support Internet Explorer 11, it won't work.

You need to encode the # character that's used when setting the color using the fill attribute on the SVG element. For example, something like <svg fill="#ffffff"></svg> would become <svg fill="%23ffffff"></svg> instead.

Jun 21, 2020
Per Djurner

CSS highlight effect to indicate important text

Here's a nice little yellow marker / highlight / glow effect that you can use to make text stand out on the page. It's achieved by layering text shadows on top of each other.

:root {
  --highlight: rgba(255, 193, 7, 0.5);
}

strong {
  font-weight: 500;
  text-shadow:
    -0.5em 0 1em var(--highlight),
    0.5em 0 1em var(--highlight),
    1em 0 1em var(--highlight),
    -1em 0 1em var(--highlight);
}

You can tweak the shadows to make the effect more similar to a yellow marker by decreasing the blur radius (the third value in all the shadows) and increasing the opacity of the color.

As a small side note, I've chosen to use the strong element for how I use this effect on my blog. I feel this is the most semantically correct HTML element to use since my intention is to indicate that a portion of the text is more important than the rest of it.

I've noticed that it's quite common to use the mark element for this sort of thing as well. However, I'm not sure that it's the most semantically correct HTML element to use in this case. The HTML specification says that "when used in the main prose of a document, it indicates a part of the document that has been highlighted due to its likely relevance to the user's current activity". There's no relevance to what the user is doing in this case, which is why I prefer the strong element instead.

If you are marking something up that has to do with the user's activity, it would be totally fine to use the mark element though. One common use case of that is when the user is doing a search and you want to highlight the text the user searched for within the result text.

Jun 16, 2020
Per Djurner

Query to get customers and the items from their last purchase

Let's say you want to get a list of all customers in your database and also include all items from their last purchase. How would you do it?

Here's one way to do it using a sub query.

SELECT customers.firstname, customers.lastname, purchases.createdat, items.name
FROM customers
INNER JOIN
(
  SELECT customerid, MAX(createdat) AS createdat
  FROM purchases
  GROUP BY customerid
) AS lastpurchase ON customers.id = lastpurchase.customerid
INNER JOIN purchases ON lastpurchase.customerid = purchases.customerid AND lastpurchase.createdat = purchases.createdat
INNER JOIN items ON purchases.id = items.purchaseid

I like this solution because the query is easy to read and understand. However, there are other ways to accomplish the same thing. One improvement was suggested by Jacob Bogers on Twitter. You can speed up the query by replacing the sub query portion of the query above with the following SQL instead.

SELECT customerid, createdat
FROM purchases a
WHERE NOT EXISTS
(
  SELECT 1
  FROM purchases b
  WHERE a.customerid = b.customerid AND a.createdat < b.createdat
)

Some other considerations.

Jun 15, 2020
Per Djurner

Ideal number of characters per line

To make your website text as readable as possible it's important to use the right number of characters to display on each line.

If your lines are really long, the reader will have a hard time finding the next line to read (because it's too far away from the beginning). If they're too short however, the reader will lose their rhythm because their eyes will have to track back too often.

But is there a magic number to aim for?

Most experts seem to indicate that having anywhere from 45 to 120 characters per line is fine. In other words, it won't cause the reader any noticeable problems if you keep it within that range. 55 to 65 characters is the most optimal for print, but there are studies that seem to suggest you can get away with a slightly higher number of characters on the web. The official W3C recommendation is to keep line length under 80 characters.

Given all that, I tend to aim for around 70 characters per line.

I do make an exception when targeting phones though. Rather than making the font really small to try and accommodate the 70 characters per line rule, I'm ok with ending up at around 50 characters per line instead.

Jun 11, 2020
Per Djurner

Sanitize HTML using JavaScript

If you're writing JavaScript and need to sanitize a string, here's an easy way to do it.

const html = "<p>Test</p>";
const elm = document.createElement("p");
elm.textContent = html;
const sanitized = elm.innerHTML;

The reason this works is that when you set the potentially dangerous string to the element's textContent property, it will be escaped. Then you just read it back using innerHTML and you have a safe string that you can use instead.

Jun 9, 2020
Per Djurner

New blog-like thing

Hello world!

I will be using this little space I've created here mainly as a way to write down notes on web development to future me. Rather than searching through old projects for code, I'll start posting here so I always have it handy.

The site itself is very much a work in progress. At the moment I'm using the GitHub issues API as the backend for the blog. In other words, I post an issue there, it ends up here. The frontend is plain HTML, CSS and JavaScript. No frameworks, no bundling. Nice and simple.

If you're interested in doing something similar, you can have a look at the repository, and if you have any thoughts, get in touch with me through Twitter.

Thanks for reading.