Camen Design

c share + remix css3-hyperlinks 3.5 KB

Under the Hood #2:
Internal / External Links, the CSS3 Way

If, unlike me, you don’t have a sixth-sense that means you know when a link will be internal / external, or will open in a new window, it is increasingly common practice to add little images to links to show that they lead to external websites. The benefit is that one can queue up external links in background-tabs, or avoid things not interested in.

For example, external links in Wikipedia:

A screenshot of links on a Wikipedia article. image from welie.com/patterns/showPattern.php?patternID=outgoing-links

There are a number of things I wish to alert my users to on this website through the link scheme.
Float over each of the example links for a demonstration.

An internal link, to another page on this site
The dotted-line is used to signify a weaker hyperlink that does not break the boundary of this website.
A link to an external website
A normal hyperlink is used to represent the interlinked ’Web, going from one site to another.
The image is not shown until the mouse is placed over the link so as to reduce visual clutter and to not add stutter to the reading rhythm. The image juts out to the left, so as to not cause the text to spasm about and break the reading flow, nor is it placed to the right where it may cover up the next word, and thus also break the reading flow.
Some links to popular websites, and a redirect
Adding the favicon to external links to some sites will help the user recognise what sort of content they will be lead to. In some cases this will better help them decide if the link is useful or a waste of time, and what sort of context is meant when the link text is not descriptive of what it is.
A link to an email address, and an application protocol
Links to other protocols may cause programs on the user’s computer to launch, or may require them to copy & paste the link into a piece of software.
A direct link to a file, rather than a webpage
Links will behave differently when leading directly to a file. Users need to be made aware of this, especially if they want to avoid PDF links, or to proceed with due caution. A link to a file is a lot like an enclosure in an email; it should be distinctly marked with some icon to show its type. As a bonus, if you’re using Firefox, that icon above will be the one from your computer for that filetype.

Beginning With Good Markup

Although all of this can be done without any additional markup than just the href, the CSS would be 10× larger. It could be done with a set of CSS classes, but then this website has no classes, and ultimately these link effects should be zero-maintenance and automatic.

We can reduce the length of selectors needed by using a few HTML attributes that have been around for ages.

<a href="http://…" rel="external" />
The rel attribute defines the relationship between this page and the linked page.
In this case we are stating that the page linked to is external.
<a href="a.pdf" type="application/pdf" />
The same as when specifying a stylesheet or javascript file in the <head>, you can provide the mime-type of the content being linked to.

These are both clean and meaningful ways to markup links in a way that robots can understand, and doesn’t rely upon class names that tie you to your design, and won’t work interchangeably with syndicated content on other people’s sites.

I could type these extra attributes manually as I write my articles, but I knew that I’d miss one or two here and there, and I’d prefer something a bit more automatic.

Automatic Markup With PHP

Here is some code that searches for links starting with “http” and adds “rel="external"” to the tag. Internal links are relative, (e.g. “href="?blog"” and don’t contain my domain name, but the code can be easily modified to look for links that don’t start with your own domain name if your CMS always writes full URLs - even to internal pages)

//add `rel="external"` to outside links:
$content = preg_replace_callback (
	//this finds links that begin with a protocol, e.g. "http"
	'/<a[^>]*href="(?:[a-z]+):[^"]+"[^>]*>/',
	//this does the substitution, either adding a rel attribute, or appending "external" to an existing one
	create_function ('$m',
		'return (strpos($m[0],"rel=\"")!==false)'.		//does 'rel="..."' already exist?
		'?str_replace("rel=\"","rel=\"external ",$m[0])'.	//insert "external" into `rel`
		':str_replace("<a ","<a rel=\"external\" ",$m[0]);'	//add `rel="external"`
	), $content
);

The second example here, is finding links that lead directly to a file, rather than a page:

//add 'type="mime/type"' to links in the content:
$content = preg_replace_callback (
	//this regex finds links to the listed file types, and adds 'type="mime/type"'
	'/<a([^>]*)href="([^"]+)\.(gif|jpg|png|pdf|zip|exe)"([^>]*)>/',
	//this does the insertion, recreating the link, with the added attribute
	create_function ('$m',
		'return "<a type=\"".mimeType($m[3])."\"${m[1]}href=\"${m[2]}.${m[3]}\"${m[4]}>";'
	), $content
);

//the "mimeType" function called above, which returns a mime-type from a file-extension
function mimeType ($extension) {
	switch ($extension) {
		case 'gif':	return 'image/gif';			break;
		case 'jpg':	return 'image/jpeg';			break;
		case 'png':	return 'image/png';			break;
		case 'pdf':	return 'application/pdf';		break;
		case 'zip':	return 'application/zip';		break;
		case 'exe':	return 'application/octet-stream';	break;
	}
}

The CSS

Internal Links

For links that are not external… which is easy now that they are automatically marked up by the PHP.
(A description of the CSS3 selectors used can be found here)

a:not([rel~="external"]) {
	text-decoration: none; border-bottom: dotted 1px;
}

A colour is not given on the border-bottom attribute so as to keep the existing link colour - even the user’s chosen browser link colour if the link colour has not been overridden anywhere.

Links to Files

We will cover these next, as the CSS for external links makes reference to these.

a[type]		{padding: 0 5px 0 25px; text-decoration: none;
		 /* start with the default "unknown file-type" icon */
		 background: #dedede url("/design/icons/page_white.png") no-repeat 5px 50%;
		 /* rounded, borders. the bottom border is removed for if the link is internal */
		 -moz-border-radius: 4px; -webkit-border-radius: 4px; border-bottom: 0 !important;}
a[type]:hover	{background-color: #eea;}

/* these icons © Mark James, <famfamfam.com/lab/icons/silk> */
a[href$=".gif"], a[href$=".jpg"], a[href$=".png"]
		{background-image: url("/design/icons/page_white_picture.png");}
a[href$=".pdf"]	{background-image: url("/design/icons/page_white_acrobat.png");}
a[href$=".zip"]	{background-image: url("/design/icons/page_white_zip.png");}
a[href$=".exe"]	{background-image: url("/design/icons/application_xp_terminal.png");}

/* Firefox users will get their own native icons from their OS.
   I’m sure this can be done in Safari, but I don’t know how */
@-moz-document url-prefix() {
	/* `@moz-document` isolates the following CSS for Firefox (gecko) only */
	/* get the "unknown file-type" icon from the OS */
	a[type]		{background-image: url("moz-icon://.?size=16");}
	/* and the other file type icons */
	a[href$=".gif"]	{background-image: url("moz-icon://.GIF?size=16");}
	a[href$=".jpg"]	{background-image: url("moz-icon://.JPG?size=16");}
	a[href$=".png"]	{background-image: url("moz-icon://.PNG?size=16");}
	a[href$=".pdf"]	{background-image: url("moz-icon://.PDF?size=16");}
	a[href$=".zip"]	{background-image: url("moz-icon://.ZIP?size=16");}
	a[href$=".exe"]	{background-image: url("moz-icon://.EXE?size=16");}
}

External Links

External links already have the underline as part of the defaults.

/* set the default external-link icon (this icon taken from Wikipedia) */
a[rel~="external"]:not([type]) {
	background: url('/design/icons/external.png') no-repeat 0 50%;
}
/* hide the icon when not hovering on the link (whilst keeping the icon on standby)
   `:not([type])` is needed to not break the file-links which already have an image */
a[rel~="external"]:not([type]):not(:hover) {
	background-image: none;
}
/* when you hover over the link, jut the favicon over the left side */
a[rel~="external"]:not([type]):hover {
	/* `background-color` is set to prevent text clashing with heavily transparent favicons, like Google’s */
	margin-left: -18px; padding-left: 18px; background-color: #fcfcfc;
}

/* some favicons for common websites I link to.
   the `:hover` is only required by Safari to prevent it from preloading these */
a[href*="apple."]:hover		{background-image: url('http://apple.com/favicon.ico');}
a[href*="archive.org"]:hover	{background-image: url('http://web.archive.org/favicon.ico');}
a[href*="deviantart."]:hover	{background-image: url('http://i.deviantart.com/icons/favicon.png');}
a[href*="google."]:hover	{background-image: url('http://google.com/favicon.ico');}
a[href*="osnews."]:hover	{background-image: url('http://osnews.com/favicon.ico');}
a[href*="php.net"]:hover	{background-image: url('http://static.php.net/www.php.net/favicon.ico');}
a[href*="slashdot."]:hover	{background-image: url('http://slashdot.org/favicon.ico');}
a[href*="tinyurl."]:hover	{background-image: url('http://tinyurl.com/favicon.ico');}
a[href*="wikipedia."]:hover	{background-image: url('http://en.wikipedia.org/favicon.ico');}
a[href*="youtube."]:hover	{background-image: url('http://s.ytimg.com/yt/favicon-vfl1123.ico');}

/* icons for other protocols */
a[href^="mailto:"]:hover	{background-image: url('/design/icons/email.png');}
a[href^="itms:"]:hover		{background-image: url('/design/icons/itms.png');}

Enjoy.

Limitations

Requires the :not CSS3 selector, available in Firefox, Safari & Opera 9.5.
As you can imagine, this does not work in Internet Explorer. But then as you know, I don’t care.