MOUSELING.net

Pure CSS to distinguish out-links from in-links

icon

Here's a fun problem I came across some time ago, while designing my site cattle mutilation: I wanted to distinguish out-links from in-links visually... without manually adding a new class to all my out-links... without any nasty JavaScript involved. There was always the option of just using JS, linking a little script in the header of all my pages and calling it a day, but I was steadfast on the aesthetics being immediate and without such bloat.

So, intent on doing everything I could to make this happen, I took to Google. I asked it: How do you make an outlink styled differently? That didn't lead to much that was relevant. Eventually, I discovered something wonderful which I didn't know I was already using: CSS pseudo-classes. Furthermore, I discovered a new type of selector to go with class and ID selectors, the humble attribute selector.

If you've been making websites for a bit, you have undoubtedly used a pseudo-class at least once in your CSS, or at least thought of it. Take this example:

a { 
    color: blue; 
} 
a:hover { 
    color: lightblue; 
}

Do you see that? The :hover part, the part that says if you're hovering over this element, make it change colour, that's a pseudo-class.

To properly define what a pseudo-class is: it's a selector on an element that says IF it has this, THEN apply these styles. IF you're hovering over this link, THEN make it light blue. But how is this relevant to making outlinks look different?

In my case, I wanted my outlinks to have the character written after them, to make them immediately jump out. This is easily done with the ::after selector, with the content set to "↗":

a::after {
    content: "↗"
}

(To be clear, this one's a pseudo-element—it has two colons—but that's not what our focus is.)

But there's a glaring problem here—this now puts it on every single link. How are these two types of links supposed to be distinct if they all have this symbol? Surely we can do better than that. Lucky for us, there's more selectors that prove useful in this scenario.

Let's think: a computer does not really know what the difference between an out-link and an in-link is. It knows what a link is, theoretically, and it knows if there's not some text followed by the character : in the link (as in http: or mailto:) then it should send you off-site, but there's no CSS selector for "a website".

Generally, though, websites are linked through HTTP (Hyper Text Transfer Protocol). Therefore, they all begin with the letters http, or https (for HTTP Secure). So why don't we tell the computer to look for those letters in the <a> element?

a[href^="http"]::after {
    content: "↗"
}

I hear you ask: What's that fancy thing!? It almost looks like actual code! It might look scary, but it's actually very simple.

This, in the square brackets, is an attribute selector. This means that, instead of applying styles based on the type of element (the plain a in this example) or the class of an element (say, .red for a class that makes something red), it decides where the style goes based on an attribute the element has.

In this case, the attribute is the href part—the part which tells you where the link is headed. Without this, you can't click the link, because it doesn't go anywhere.

Here's the fun part: if we were to use [href] on its own, it would target every single <a> element with a href attribute would be selected. We don't want this because it is, effectively, the same as before. So the other parts come in.

Here ^="http" tells the computer, ONLY do this IF the href value starts with the letters "http". The caret before the equals sign makes it "if it starts with http" rather than "if it is http", because links are more than just those four letters.

Now we have finally created a definition the computer can understand: if a link points somewhere starting with the letters "http", then put ↗ after it. Isn't that wonderful? It's one simple rule that works.

...but another problem arises: I don't want this character to appear after images that are links, for example buttons. What now?

Now we triple up on the pseudoclasses. To make things quick, I'll introduce two at once.

a[href^="http"]:not(:has(img))::after {
    content: "↗"
}

Are you getting a sense for this yet? The first pseudo-class, :not(), tells the computer to do this, but NOT if it's this. You have to put something inside those parentheses for it to be of any use, though, which is where :has() comes in. The img inside of that is, well, the thing we don't want that symbol to appear after: images.

Our new definition has become: if a link points somewhere starting with the letters "http", and the link does NOT have an image inside of it, then put ↗ after it.

And so it finally works for my purposes. You're free to use this code if you want, alter it, and so on—I added some extra styling on my own site. I'm hoping that this tutorial will prove useful to someone, or at least be interesting to others. I think it's a really lovely solution.