Be a pro: use font embedding, not font linking

If you want to use a font on your website you can load it by linking to an external server (using CSS or JavaScript). This is common practice and you will probably know about it if you worked with Google Fonts or Adobe Typekit. This is what we call “font linking”. The alternative is that you host the font yourself and use @font-face in your CSS to load it. You will need to upload the font in several formats to your server. This self-hosted approach is also called “font embedding”.

What is the difference?

With font linking you add the following HTML code to your website:

<link href='http://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>

While with font embedding you add the following CSS code:

@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 400;
  src: url('open-sans-latin-regular.eot'); /* IE9 Compat Modes */
  src: local('Open Sans'), local('OpenSans'),
       url('open-sans-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
       url('open-sans-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */
       url('open-sans-latin-regular.woff') format('woff'), /* Modern Browsers */
       url('open-sans-latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */
       url('open-sans-latin-regular.svg#OpenSans') format('svg'); /* Legacy iOS */
}

As you can see it easier to link the font as you do not have to write extensive CSS and upload the 5 font files (eot, woff2, woff, ttf & svg) that font embedding requires.

Font linking is not allowed

Font linking does not work for offline content. It requires requests to other services, in contradiction to font embedding. Font linking may cause uptime worries, dependency issues (Great firewall of China) and leaking of Personally Identifiable Information (PII). In some countries (like the Netherlands) it is even forbidden by law to share PII (like IP address and user-agent string) without an explicit consent from the user to allow tracking. So, it is a simple choice, one would think, right?

Font embedding is also not allowed

Services like Fonts.com, MyFonts, Typekit, etc. do not allow font embedding, you need to link them. The reason: they have a “pay-per-use” business model. But isn’t it a bit strange that this type of usage (enforced by the licensing model) is actually restricted by EU privacy laws? Exception is Google Fonts as their fonts are free to use and free to embed.

It’s-a me, Mario! Let’s-a go!

Mario Ranftl (majodev) has created an extremely useful google-webfonts-helper (hosted on Heroku). If you want to know how you can find the source on Github (collecting stars). It makes it very easy to self-host your fonts. The steps:

  1. Go to: https://google-webfonts-helper.herokuapp.com/fonts
  2. Select one of the 682 fonts from the menu on the left
  3. Copy-paste presented CSS code into your stylesheet in the directory “css”
  4. Download the zip file using the big blue button
  5. Unzip the files and upload them to your website in the directory “fonts”

Thank you Mario, that is super! Alternatively, if you have your own fonts and need them in such a convenient zip file, you may try fontsquirrel.com’s Webfont Generator. Let me know how you like these tools (or if you know any better) using the comments. Also, check out the discussion on Hacker News!

Now let’s start using fonts responsibly!

PHP asset proxy increases website availability

remote_assets

Don’t you hate it when your site does not work, because you linked jQuery from “code.jquery.com” and that site is suffering connection problems? This may also happen with stylesheets or with font files. To counter this problem (but not lose the convenience of remote loaded assets) I created  an “asset proxy” in PHP. It will cache the assets in a cache folder on your web server, so that you do not have to worry about downtime of other services. You can configure how often the cache should be refreshed. When the external source is not available during a refresh the stale cache files will be used and there is no downtime at all!

proxy_assets

Install asset-proxy.php in your webroot. Then replace all references in your HTML from:

 href="http://fonts.googleapis.com/css?family=Droid+Sans:400,700"

to:

 href="/asset-proxy.php/fonts.googleapis.com/css?family=Droid+Sans:400,700"

Make sure you edit the list of allowed hostnames in the header of the PHP file and that you set an appropriate refresh time (in seconds). If the assets are not available upon refresh the stale files are served.

// hostnames for which "GET" requests can be proxied over "HTTP" (no ssl)
$hostnames = array(
	'fonts.gstatic.com',
	'maxcdn.bootstrapcdn.com',
	'netdna.bootstrapcdn.com',
	'fonts.googleapis.com',
	'ajax.googleapis.com',
);

// maximum age of a file before being refreshed
$refresh_age = 24*3600;

// directory where the cache resides (should exist and not be served)
$cache_dir = '/tmp/cache';

// strip the leading "/proxy.php/" from the URL
$url = substr($_SERVER['REQUEST_URI'], strlen($_SERVER['SCRIPT_NAME'].'/'));

// if there is no URL specified show bad request error
if(!$url || !strpos($url,'/')){
	header('Bad Request', true, 400);
	exit;
}

// get the hostname which should be the first segment (until the first slash)
$hostname = substr($url, 0, strpos($url, '/'));

// if the hostname is not in the list of allowed hostnames show forbidden error
if (!in_array($hostname, $hostnames)) {
	header('Forbidden', true, 403);
	exit;
}

// calculate the cached filename and check whether it already exists
$filename = $cache_dir.'/'.md5($url);
$file_exists = file_exists($filename);

// get the file age if the file exists
if ($file_exists) {
	$file_age = time()-filemtime($filename);
}

// if cache exists and is fresh, let's read the file, else retrieve it with cURL
if ($file_exists && $file_age<$refresh_age) {
	$result = file_get_contents($filename);
} else {
	// set some headers on the cURL call to pretend we are a user
	$sent_headers = array();
	foreach (array('User-Agent','Accept','Accept-Language','Referer') as $header) {
		$key = 'HTTP_'.strtoupper(str_replace('-','_',$header));
		if (isset($_SERVER[$key])) {
			$sent_headers[] = $header.': '.$_SERVER[$key];
		}
	}

	// make sure we do net get chunked, deflated or gzipped content
	$sent_headers[] = 'Accept-Encoding: ';
	$sent_headers[] = 'Cache-Control: max-age=0';
	$sent_headers[] = 'Connection: keep-alive';

	// initialize cURL with the URL, our headers and set headers retrieval on
	$curl = curl_init('http://'.$url);
	curl_setopt_array($curl, array(
			CURLOPT_HEADER => true,
			CURLOPT_RETURNTRANSFER => true,
			CURLOPT_BINARYTRANSFER => true,
			CURLOPT_HTTPHEADER => $sent_headers
	));

	// execute cURL call and get status code
	$result = curl_exec($curl);
	$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
	curl_close($curl);

	if ($status == 200) {
		// file was successfully retrieved
		if (file_put_contents($filename, $result)===false) {
			// show error on unsuccessful write
			header('Internal Server Error', true, 500);
			exit;
		}
	} else if ($file_exists) {
		// serve stale
		$result = file_get_contents($filename);
		// reset refresh timer
		touch($filename);
	}

}

// split the message in raw headers and body
if (strpos($result,"\r\n\r\n")!==false) {
	list($raw_headers,$body) = explode("\r\n\r\n", $result, 2);
} else {
	list($raw_headers,$body) = array($result,'');
}

// convert raw headers into an array
$raw_headers = explode("\n", $raw_headers);

// parse raw headers into received headers
$received_headers = array();
foreach ($raw_headers as $h) {
	$h = explode(':', $h, 2);
	if (isset($h[1])) {
		$received_headers[$h[0]] = trim($h[1]);
	}
}

// set certain headers for the output
$headers = array('Content-Type','Content-Encoding','Cache-Control','ETag','Last-Modified','Vary');
foreach ($headers as $header) {
	if (isset($received_headers[$header])) {
		header($header.': '.$received_headers[$header]);
	}
}

// replace the absolute URL's in the output
foreach ($hostnames as $hostname) {
	$body = preg_replace('/(https?:)?\/\/'.str_replace('.','\.',$hostname).'\//',
		$_SERVER['SCRIPT_NAME'].'/'.$hostname.'/', $body);
}

// set the new content length properly
header('Content-Length: '.strlen($body));

// echo the contents of the body
echo $body;

Best thing since sliced bread.. 😉 And only 128 lines of PHP code! Source code is on Github:

https://github.com/mevdschee/asset-proxy.php

 

Drawing with CSS3

The latest CSS specification (also known as CSS3) has introduced a set of new properties which allow the developer to implement a lot of interesting effects on a web page. It is now possible to have round corners, shadows or even create nice background patterns. Inspired by others who had done amazing things with CSS3 we wanted to give it a try ourselves in order to get some first hand exposure to the new properties and see how much effort was required to do a little experiment with CSS3. We decided to try to draw our company’s logo using only CSS3.

This is the original logo:

How can you create such an image?

When drawing something with CSS, you first need to slice your image in smaller pieces (usually rectangles) and then separately implement every part of the image with CSS. Then you just need to position the pieces correctly so that it forms an image.

For example, shapes can be rendered styling only a <div> element:

-> a circle

#circle {
  background-color: red;
  border-radius: 50%;
  width: 100px;
  height: 100px;  /* if set to 200px it becomes an elipse for example */
}

-> one or multiple triangles

#triangle {
  /* border-color: transparent transparent red transparent; /* red triangle*/
  border-color: green blue red orange; /* multiple triangles*/
  border-style: solid;
  border-width: 100px;
  width: 0px;
  height: 0px;
} 

-> outlines:

 #outlines {
  background-color: orange;
  box-shadow: 0 0 0 10px red, 0 0 0 20px blue, 0 0 0 30px green, 0 0 0 40px silver;
  width: 20px;
  height: 20px;
} 

-> multi-columns:

#columns {
  /* vendor prefixes for gradients are required as currently no browser implements it without prefixes */
  background: linear-gradient(0, orange 50%, green 50%);
  background-size: 50px;
  width: 200px;
  height: 100px;
}

-> background patterns:

#patterns {
  /* note vendor prefixes for gradients are required */
  background: linear-gradient(45deg, silver 25%, orange 25%, orange 50%, silver 50%, silver 75%, orange 75%, orange);
  background-size: 50px;
  width: 200px;
  height: 200px;
} 

The image below displays the resulting drawings created with the above codes.

If you have a modern browser, thanks to Lea Verou‘s interactive CSS playground dabblet you can check on this page how it would be rendered in your browser (some additional shapes are included too) as well as edit the CSS.

Now that we have seen the kind of things that can be achieved with the new properties on a single <div> element, let’s go back to our logo. We initially thought that we could try to create a flexible CSS3 image that would render properly when the logo would be scaled to different sizes. We tried to use percentages to set all the sizes instead of pixels. Unfortunately, this did not work well as it turned out that browsers handle percentages differently. On this page you can find an image that shows how different browsers rendered differently the logo when implemented using percentages.

This is just an example of what can go wrong with different implementations of the standard and why it is important to test with different browsers to ensure that the user gets the same experience independently of the browser they are using.

If we stick to fixed sizes (in pixels) the result provides a consistent experience in all the browsers we tests. You can take a look at the result: the CSS3 LeaseWeb logo!

It took me a couple of hours to come up with the first structure and a couple more for the tweaking. With some CSS knowledge, a bit of patience and creativity you can draw amazing images with CSS3.

Developing a mobile web application with IUI

At LeaseWeb we have a hosting control panel called “Self Service Center” or SSC. We are starting to make our first steps into making the Self Service Center available on a mobile platform.

We have a couple of options on how to implement this:
• Build native applications for each mobile platform ( mainly Android & iOS )
• Build a mobile accessible website

We had to make a list of advantages and disadvantages of the 2 options:

We prefer to keep a single code stack, for maintenance reasons, and so we can make every feature immediately available in the mobile version. This is why we decided to make a proof-of-concept of a mobile accessible website.

The first problem we encountered was to find out how to implement this mobile website the best way possible in the Symfony framework. At first, we checked if this was possible by doing only CSS changes, so without touching the templates. It did not took us long to come to the conclusion that this was not enough and we had to revise all of the templates.

By looking up the API of the Symfony framework on a mobile device we saw that they had a mobile version of it on their website. We were very curious what technology they used and how they implemented it, so we started digging. We found out that they were using the IUI framework on their mobile API page and although it didn’t work flawlessly we were impressed with the way it worked.

So what is this IUI and how does it work?
With IUI you have to think of your webpage as 1 big HTML file which you keep adding content to. So you keep updating your current HTML with new adaptations through AJAX request. When you click a button / link the new page is loaded off-screen. When the screen is loaded it slides smoothly into your screen. The old screens are not unloaded so you can smoothly slide back to the previous screen using the back button in the upper left corner.

Figure 1 – The red box shows the area that is visible on the screen, the other pages can slide in.

Using this framework you can make any webpage feel like a native iPhone application. A single IUI web page can contain multiple related iPhone screens. This makes IUI map on the Symfony MVC framework quite nicely, because all the data of a single MVC view will not fit on one iPhone screen, but it might fit on multiple.

Also for backend developers it is really nice that you don’t have to design all the screen layouts. You can just follow the layout that is provided with the framework and your screens immediately look professional. Although we created an impressive proof-of-concept there were some problems we did encounter:

A broken back button
When you post a form with validation errors, the same page – containing the form – is loaded with the errors displayed next to the input fields. This new page has the same id as the old form causing the back functionality to be broken.

The IUI back functionality works using a “stack”. This stack contains the ids of the previously loaded pages and forms. When the page loads it adds the id to the stack. When a validation error has occurred the stack contains the wrong values, because the validated form and the original form have the same id.

The possible solutions we found are:
1) To fix this we can give the validated form a different id than the original form.
2) Show the (validation) errors on a new page or popup.

An iPhone like way to handle field validation error is to show popups containing the validation errors. This can be achieved in IUI using “Dialog” type forms. First we tried using JavaScript “alert” calls but we had trouble, because these pop-ups are blocking and it is hard to use them without modifying IUI internals.

Displaying flash messages
More or less the same problem occurs when displaying “flash” messages. “Flash” messages are messages often colored yellow, red or green (for warnings, errors and confirmations) and are set on top of the screen displaying the results of the last page load. In IUI the flash messages are not working properly because pages are not reloaded on “back button” navigation. In an iPhone application these messages typically show in a popup.

Conclusion
We think IUI is a good and easy way to convert your current website to a mobile website in a MVC framework. Problems we encountered are related to error and status messages that a typical web application shows inside (and on top of) forms. Unfortunately IUI does not seem prepared for showing these. We’ve found a workaround by showing these messages in “Dialog” type forms.

LeaseWeb Self Service Center – https://secure.leaseweb.com
IUI framework – http://www.iui-js.org/
Symfony framework – http://www.symfony-project.org