<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Jonathan Nicol</title>
	<atom:link href="https://jonathannicol.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://jonathannicol.com</link>
	<description>Adventures in web design and development</description>
	<lastBuildDate>Mon, 08 Feb 2021 00:48:36 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0</generator>
	<item>
		<title>Fixed-height elements with an intrinsic ratio</title>
		<link>https://jonathannicol.com/blog/2021/02/03/fixed-height-elements-with-an-intrinsic-ratio/</link>
		
		<dc:creator><![CDATA[Jonathan]]></dc:creator>
		<pubDate>Thu, 04 Feb 2021 03:15:46 +0000</pubDate>
				<category><![CDATA[CSS]]></category>
		<guid isPermaLink="false">https://jonathannicol.com/?p=3445</guid>

					<description><![CDATA[The padding-bottom technique is perfect when you need an HTML element to have an intrinsic ratio, provided the element is 100% wide. But it doesn&#8217;t work the other way around, when you want to set the element’s width as a percentage of its height. At least not without a helping hand from JavaScript. Here is [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>The <a href="https://css-tricks.com/aspect-ratio-boxes/">padding-bottom technique</a> is perfect when you need an HTML element to have an intrinsic ratio, provided the element is 100% wide. But it doesn&#8217;t work the other way around, when you want to set the element’s width as a percentage of its height. At least not without a helping hand from JavaScript.</p>
<p>Here is a real world example of when this might be useful, an image carousel where the slides have a fixed height and variable widths:</p>
<p class="post-img"><img fetchpriority="high" decoding="async" class="alignnone size-full wp-image-3459" src="https://jonathannicol.com/cms/wp-content/uploads/2021/02/ap-carousel.jpg" alt="" width="1800" height="801" srcset="https://jonathannicol.com/cms/wp-content/uploads/2021/02/ap-carousel.jpg 1800w, https://jonathannicol.com/cms/wp-content/uploads/2021/02/ap-carousel-300x134.jpg 300w, https://jonathannicol.com/cms/wp-content/uploads/2021/02/ap-carousel-1024x456.jpg 1024w, https://jonathannicol.com/cms/wp-content/uploads/2021/02/ap-carousel-768x342.jpg 768w, https://jonathannicol.com/cms/wp-content/uploads/2021/02/ap-carousel-1536x684.jpg 1536w" sizes="(max-width: 1800px) 100vw, 1800px" /></p>
<p>At different viewport widths the carousel is taller, so we can&#8217;t simply hardcode each slide&#8217;s width. Before today I would have told you that the only reliable way of scaling the slides was to pass their aspect ratios to JavaScript, calculate the width of each slide relative to its height, then resize them accordingly. And to do that for every slide, every time the viewport is resized. It would be much simpler if the slides could be scaled once by CSS, but to achieve that we need a way to give each slide a relative width.</p>
<p><span id="more-3445"></span></p>
<h2>The solution: a canvas shim</h2>
<p>My solution is to use a transparent canvas element to force the aspect ratio. The key to this technique is the fact that if you define width and height attributes for a canvas, then it keeps the same aspect ratio when you change its height with CSS (the same is not true of the img element, which is is why I didn&#8217;t use a transparent PNG or GIF as a shim).</p>
<pre><code class="language-markup">&lt;div class="intrinsic-ratio"&gt;
  &lt;canvas class="ratio-shim" width="200" height="100"&gt;&lt;/canvas&gt;
  &lt;img src="my-image.jpg"&gt;
&lt;/div&gt;</code></pre>
<pre><code class="language-css">.intrinsic-ratio {
  position: relative;
  display: table-cell;
  width: auto;
  height: 200px; /* height can be any value */
}
.ratio-shim {
  height: 100%;
}
/* The content could be anything. In this case an img */
.intrinsic-ratio img {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}</code></pre>
<p>So yeah, an extra HTML element is required for this to work, but that&#8217;s not so awful when you consider that the alternative is measuring and resizing the element with JavaScript.</p>
<p>You might also notice that the wrapping element has its display property set to <code>table-cell</code>. This makes it shrink-wrap to the width of its content. <code>display: inline-block</code> would work too, as would floating the element or giving it absolute positioning.</p>
<p>I don&#8217;t imagine that I am the first person to come up with this technique, but it&#8217;s not one I&#8217;ve seen in the wild so it might be worth keeping in the toolbelt until browsers have <a href="https://www.bram.us/2020/11/30/native-aspect-ratio-boxes-in-css-thanks-to-aspect-ratio/" rel="noopener" target="_blank">native support for aspect-ratio</a>.</p>
<h2>Demo</h2>
<p><a href="https://codepen.io/jnicol/pen/eYBpKKg" target="_blank" rel="noopener">View a demo on CodePen</a></p>
<h2>Notes</h2>
<p>The CSS padding technique doesn&#8217;t work when an element&#8217;s width is unknown because percentage-based padding is relative to the <em>width</em> of the containing element, not its height. When you only know the height of the element, setting <code>padding-left</code> or <code>padding-right</code> as a percentage will produce inconsistent results.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Local &#8220;Lightning&#8221; first impressions</title>
		<link>https://jonathannicol.com/blog/2020/02/27/local-lightning-first-impressions/</link>
					<comments>https://jonathannicol.com/blog/2020/02/27/local-lightning-first-impressions/#comments</comments>
		
		<dc:creator><![CDATA[Jonathan]]></dc:creator>
		<pubDate>Fri, 28 Feb 2020 04:03:39 +0000</pubDate>
				<category><![CDATA[Server Admin]]></category>
		<category><![CDATA[Toolbox]]></category>
		<category><![CDATA[Wordpress]]></category>
		<guid isPermaLink="false">https://jonathannicol.com/?p=3396</guid>

					<description><![CDATA[Local “Lightning” is the latest iteration of Flywheel’s popular local development environment. Local is a suite of web server components that allows you to run PHP applications on your computer &#8211; a vast improvement on the bad-old-days when we had to FTP files to a remote server to test our work. Shudder. Flywheel is a [&#8230;]]]></description>
										<content:encoded><![CDATA[<p><a href="https://localwp.com/" rel="noopener noreferrer" target="_blank">Local “Lightning”</a> is the latest iteration of Flywheel’s popular local development environment. Local is a suite of web server components that allows you to run PHP applications on your computer &#8211; a vast improvement on the bad-old-days when we had to FTP files to a remote server to test our work. Shudder.</p>
<p>Flywheel is a WordPress hosting provider so Local is aimed at WordPress developers, but can theoretically be used for any PHP application regardless of where it will be hosted. </p>
<p><img decoding="async" width="2560" height="1777" src="https://jonathannicol.com/cms/wp-content/uploads/2020/02/local-site-scaled.jpg" alt="Local site" class="alignnone size-full wp-image-3405" srcset="https://jonathannicol.com/cms/wp-content/uploads/2020/02/local-site-scaled.jpg 2560w, https://jonathannicol.com/cms/wp-content/uploads/2020/02/local-site-300x208.jpg 300w, https://jonathannicol.com/cms/wp-content/uploads/2020/02/local-site-1024x711.jpg 1024w, https://jonathannicol.com/cms/wp-content/uploads/2020/02/local-site-768x533.jpg 768w, https://jonathannicol.com/cms/wp-content/uploads/2020/02/local-site-1536x1066.jpg 1536w, https://jonathannicol.com/cms/wp-content/uploads/2020/02/local-site-2048x1422.jpg 2048w" sizes="(max-width: 2560px) 100vw, 2560px" /><br />
<span id="more-3396"></span><br />
I ignored Local for a long time because early versions ran inside a virtual machine. In my experience virtualisation is painfully slow, even on fast host systems, and by many accounts that was the case with Local too. In fact performance was bad enough that Flywheel decided to <a href="https://localwp.com/community/t/local-lightning-public-beta/14075" rel="noopener noreferrer" target="_blank">rebuild Local from scratch</a> as a native application. They even dubbed the new version “Lightning” to emphasise the speed boost.</p>
<p>I am a long longtime <a href="https://www.mamp.info/en/mamp-pro/mac/" rel="noopener noreferrer" target="_blank">MAMP Pro</a> user, and despite having no major complaints about MAMP I was curious about Local’s new direction and recently took it for a spin.</p>
<h2>Teething issues</h2>
<p>Unfortunately, things got off to a rocky start. After launching Local and configuring my first site, my browser hit me with a 404. After some head scratching I deduced that there was a conflict between MAMP and Local. MAMP was listening on ports 80 and 443, which are the standard HTTP and HTTPS ports, and was blocking Local from doing its thing. After quitting MAMP and restarting my computer Local’s server ran smoothly. Little did I realise that my port-hijacking woes were far from over &#8211; more on that shortly &#8211; but for now let&#8217;s focus on the positive.</p>
<p>Once I got Local up and running, its features really impressed me.</p>
<h2>Setup Wizard</h2>
<p>Installing a new site with Local takes about 30 seconds. A setup wizard asks you a few basic questions about the site &#8211; what is it called, what username and password you want for your WordPress admin user &#8211; and then it installs a fresh copy of WordPress for you, pre-configured and ready to go. It really couldn’t be simpler.</p>
<p class="post-img"><img decoding="async" width="2560" height="1777" src="https://jonathannicol.com/cms/wp-content/uploads/2020/02/local-wizard-scaled.jpg" alt="Local setup wizard" class="alignnone size-full wp-image-3408" srcset="https://jonathannicol.com/cms/wp-content/uploads/2020/02/local-wizard-scaled.jpg 2560w, https://jonathannicol.com/cms/wp-content/uploads/2020/02/local-wizard-300x208.jpg 300w, https://jonathannicol.com/cms/wp-content/uploads/2020/02/local-wizard-1024x711.jpg 1024w, https://jonathannicol.com/cms/wp-content/uploads/2020/02/local-wizard-768x533.jpg 768w, https://jonathannicol.com/cms/wp-content/uploads/2020/02/local-wizard-1536x1066.jpg 1536w, https://jonathannicol.com/cms/wp-content/uploads/2020/02/local-wizard-2048x1422.jpg 2048w" sizes="(max-width: 2560px) 100vw, 2560px" /></p>
<h2>One-click certificate trust</h2>
<p>In this day and age every website should have an SSL certificate. When you&#8217;re developing locally this may not seem important, but having a certificate ensures that your WordPress database doesn&#8217;t get polluted with http:// prefixed URLs. Plus, eCommerce applications will complain loudly if you try to make a transaction using an unsecured connection.</p>
<p>With MAMP, the process of adding a self-signed SSL certificate goes like this:</p>
<ol>
<li>Generate a certificate inside MAMP</li>
<li>Choose a location to save it on your hard drive</li>
<li>Navigate to that location, and drag the certificate into Apple&#8217;s Keychain Access app</li>
<li>Double click the certificate to edit its preference, and change the Trust setting to &#8220;always trust&#8221;</li>
<li>Type your macOS password to authorise the change</li>
</ol>
<p>I&#8217;ve done it so many times that this process takes me about a minute, but it still irks me that I need to jump through these hoops.</p>
<p>When you create a site in Local it creates a certificate for you automatically. Then, you can trust the certificate without leaving the app (trusting a certificate stops your website browser from displaying a security warning).</p>
<p>My one niggle with this feature is that Local doesn’t configure WordPress to use HTTPS by default. You’ll need to change WordPress’ home and site URL to use the https:// prefix yourself.</p>
<h2>Automatic database dumps</h2>
<p>This is a big one. After working with MAMP for years I have resigned myself to the fact that to back up a site&#8217;s local database I need to manually export it to disk. Local has an elegant solution to this problem. Anytime a site is stopped within the Local application, a database dump is performed. No more manual MySQL dumps!</p>
<p class="post-img"><img loading="lazy" decoding="async" width="2368" height="1368" src="https://jonathannicol.com/cms/wp-content/uploads/2020/02/local-db.jpg" alt="Local database dump" class="alignnone size-full wp-image-3403" srcset="https://jonathannicol.com/cms/wp-content/uploads/2020/02/local-db.jpg 2368w, https://jonathannicol.com/cms/wp-content/uploads/2020/02/local-db-300x173.jpg 300w, https://jonathannicol.com/cms/wp-content/uploads/2020/02/local-db-1024x592.jpg 1024w, https://jonathannicol.com/cms/wp-content/uploads/2020/02/local-db-768x444.jpg 768w, https://jonathannicol.com/cms/wp-content/uploads/2020/02/local-db-1536x887.jpg 1536w, https://jonathannicol.com/cms/wp-content/uploads/2020/02/local-db-2048x1183.jpg 2048w" sizes="auto, (max-width: 2368px) 100vw, 2368px" /></p>
<h2>Mailhog</h2>
<p>One of the drawbacks of developing locally is that without a mail server none of your site’s transactional email is delivered, which makes testing difficult. For instance you can’t verify that an eCommerce system is actually sending order notification emails.</p>
<p>Local comes bundled with Mailhog, a nifty application that runs a simple SMTP server that “hogs” any mail sent from your site so that you can view it later. As someone who’s gone without this feature for so long, it feels like magic.</p>
<p class="post-img"><img loading="lazy" decoding="async" width="2110" height="1315" src="https://jonathannicol.com/cms/wp-content/uploads/2020/02/mailhog.jpg" alt="Mailhog" class="alignnone size-full wp-image-3400" srcset="https://jonathannicol.com/cms/wp-content/uploads/2020/02/mailhog.jpg 2110w, https://jonathannicol.com/cms/wp-content/uploads/2020/02/mailhog-300x187.jpg 300w, https://jonathannicol.com/cms/wp-content/uploads/2020/02/mailhog-1024x638.jpg 1024w, https://jonathannicol.com/cms/wp-content/uploads/2020/02/mailhog-768x479.jpg 768w, https://jonathannicol.com/cms/wp-content/uploads/2020/02/mailhog-1536x957.jpg 1536w, https://jonathannicol.com/cms/wp-content/uploads/2020/02/mailhog-2048x1276.jpg 2048w" sizes="auto, (max-width: 2110px) 100vw, 2110px" /></p>
<h2>Config and files stored together</h2>
<p>Unlike MAMP, which stores each site&#8217;s configuration files who-knows-where, Local stores them right next to your application&#8217;s database and public files. It feels very tidy to have everything in one place.</p>
<h2>Drag-and-drop import</h2>
<p>To import a website into Local, you can simply drag a .zip file into the app. The only <a href="https://localwp.com/help-docs/how-to-import-a-wordpress-site-into-local/" rel="noopener noreferrer" target="_blank">requirement</a> is that the archive needs to contain a WordPress wp-content directory and an SQL database file.</p>
<p class="post-img"><img loading="lazy" decoding="async" width="2560" height="1777" src="https://jonathannicol.com/cms/wp-content/uploads/2020/02/local-import-scaled.jpg" alt="Local drag-and-drop import" class="alignnone size-full wp-image-3406" srcset="https://jonathannicol.com/cms/wp-content/uploads/2020/02/local-import-scaled.jpg 2560w, https://jonathannicol.com/cms/wp-content/uploads/2020/02/local-import-300x208.jpg 300w, https://jonathannicol.com/cms/wp-content/uploads/2020/02/local-import-1024x711.jpg 1024w, https://jonathannicol.com/cms/wp-content/uploads/2020/02/local-import-768x533.jpg 768w, https://jonathannicol.com/cms/wp-content/uploads/2020/02/local-import-1536x1066.jpg 1536w, https://jonathannicol.com/cms/wp-content/uploads/2020/02/local-import-2048x1422.jpg 2048w" sizes="auto, (max-width: 2560px) 100vw, 2560px" /></p>
<p>If the domain of the site you’re importing doesn’t match the .local domain you chose in Local, you will be prompted to fix the issue, which will perform a find &#038; replace in the site’s database. It gracefully handles serialized data too, which a naïve find &#038; replace won’t.</p>
<h2>Sequel Pro integration</h2>
<p>Local uses Adminer to let you administer your site’s’ MySQL databases, but it will also detect if you have Sequel Pro installed and give you a button that opens a database in Sequel. It’s a nice touch that shows Local’s developers are in touch with their user’s workflow (or they just really like Sequel Pro).</p>
<h2>Free as in beer</h2>
<p>Did I mention that you get all of this for free?! There is a <a href="https://localwp.com/pro/" rel="noopener noreferrer" target="_blank">paid version of Local</a>, but as far as I can tell its additional features only benefit Flywheel’s hosting customers.</p>
<h2>Port-hijacking hijinx</h2>
<p>Remember how I said that my port-hijacking woes weren&#8217;t over yet? I ran into more problems when I needed to fire up MAMP to work on one of my other projects. I knew that Local and MAMP didn’t play nice together, so I quit Local first, but MAMPs server still didn&#8217;t seem to be responding. For some reason Local wasn’t releasing ports 80 and 443 when I quit the application.</p>
<p>After extensive debugging I concluded that the problem is specific to macOS High Sierra, since it doesn’t affect my MacBook which runs Mojave. On High Sierra Local installs a launch daemon that starts NGINX when the OS boots up, and never stops it again. It is an obscure bug (feature?) that most people won’t notice, but it irritated me so much that I upgraded to Mojave!</p>
<h2>No subdirectory or custom table prefix</h2>
<p>Another shortcoming of Local is that it doesn’t officially support installing WordPress in a subdirectory, or choosing a custom database table prefix (which the standard WordPress installer does). </p>
<p>Ultimately not a big deal, but if you’re accustomed to changing the installation directory or table prefix then you might find Local restrictive in this respect.</p>
<h2>No hot swapping or Apache</h2>
<p>Unlike its virtualised predecessor, Local Lightning doesn’t offer an option to hot swap between different versions of PHP, or between Apache and NGINX. These features are on the roadmap, though.</p>
<h2>Verdict</h2>
<p>Overall I’m very impressed by Local. It is an elegant software suite that improves on MAMP in some important ways. Local is a little more prescriptive than I’m comfortable with, but I guess that’s the price of its simplified workflow.</p>
<p>“Lightning” has only been out of beta for a couple of weeks, so of course it’s not perfect, but the development team seem to be hard at work adding features and squashing bugs, and I am optimistic that it will continue to improve.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://jonathannicol.com/blog/2020/02/27/local-lightning-first-impressions/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Fixing CORS font errors on a Runcloud server</title>
		<link>https://jonathannicol.com/blog/2020/02/24/fixing-cors-font-errors-on-a-runcloud-server/</link>
					<comments>https://jonathannicol.com/blog/2020/02/24/fixing-cors-font-errors-on-a-runcloud-server/#comments</comments>
		
		<dc:creator><![CDATA[Jonathan]]></dc:creator>
		<pubDate>Mon, 24 Feb 2020 19:15:54 +0000</pubDate>
				<category><![CDATA[Server Admin]]></category>
		<guid isPermaLink="false">https://jonathannicol.com/?p=3358</guid>

					<description><![CDATA[If you’ve ever needed to serve fonts from a web server to a remote application, then you’ve probably run into Cross-Origin Resource Sharing (CORS) errors. When you view a web page that is attempting load fonts from your server, your browser console displays an error message similar to this: Access to https://my-server.com/font.woff from origin https://remote-server [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>If you’ve ever needed to serve fonts from a web server to a remote application, then you’ve probably run into Cross-Origin Resource Sharing (CORS) errors. When you view a web page that is attempting load fonts from your server, your browser console displays an error message similar to this:</p>
<blockquote><p>Access to https://my-server.com/font.woff from origin https://remote-server has been blocked by CORS policy: No &#8216;Access-Control-Allow-Origin&#8217; header is present on the required resource.</p></blockquote>
<p>The problem can be fixed by adding an Access-Control-Allow-Origin header to the font file, which allows access from the remote application.<br />
<span id="more-3358"></span><br />
<em>Note: Instructions below this point are specific to Runcloud and won’t work for other NGINX configurations.</em></p>
<p>If you’re operating a <a href="https://runcloud.io/" rel="noopener noreferrer" target="_blank">Runcloud</a> server then you need to edit NGINX configuration files to add that header. Runcloud already adds a location block for static files to your app&#8217;s main.conf:</p>
<pre><code class="language-none">location ~ .(ico|css|gif|jpe?g|png|gz|zip|flv|rar|wmv|avi|css|js|swf|png|htc|mpeg|mpg|txt|otf|ttf|eot|woff|woff2|svg)$ {
    expires     1M;
    include /etc/nginx-rc/conf.d/your-app-name.d/headers.conf;
    add_header  Cache-Control "public";
    include /etc/nginx-rc/extra.d/your-app-name.location.static.*.conf;
    try_files $uri @proxy;
}</code></pre>
<p><a href="https://runcloud.io/docs/guide/cheat-sheet/nginx" rel="noopener noreferrer" target="_blank">Runcloud’s documentation</a> explains that you should never edit that file directly, since your changes might be overwritten, but you can hook into the include near the end of the block to create a nested location for fonts.</p>
<p>Issue the following command to create a new NGINX configuration file, remembering to replace <code>&lt;your-app-name&gt;</code> with the name of your Runcloud web application:</p>
<pre><code class="language-bash">touch /etc/nginx-rc/extra.d/&lt;your-app-name&gt;.location.static.cors.conf</code></pre>
<p>Then open that file in a text editor such as nano:</p>
<pre><code class="language-bash">nano /etc/nginx-rc/extra.d/&lt;your-app-name&gt;.location.static.cors.conf</code></pre>
<p>Paste the following location block into the file you just created, again remembering to customise <code>&lt;your-app-name&gt;</code>:</p>
<pre><code class="language-none">location ~ .(ttf|eot|woff|woff2)$ {
    # Include default Runcloud headers for static files
    include /etc/nginx-rc/conf.d/&lt;your-app-name&gt;.d/headers.conf;
    add_header Cache-Control "public";

    # Allow fonts to be accessed from remote applications
    add_header Access-Control-Allow-Origin *;
}</code></pre>
<p>This location block uses a regular expression, indicated by the <code>~</code> modifier, to target files with a font extension, and adds an Access-Control-Allow-Origin with a wildcard <code>*</code> value. The wildcard means that any domain can access the font. You could specify a domain instead.</p>
<p>You will notice that we have included Runcloud’s default headers too. NGINX’s <code>add_header</code> nukes any headers defined outside the current scope, so we need to re-add them.</p>
<p>After saving the file, reload NGINX’s configuration by issuing the following command:</p>
<pre><code class="language-bash">systemctl reload nginx-rc</code></pre>
<p>You should notice that an Access-Control-Allow-Origin header is added to fonts when they are served. You can use cURL to test this from the command line:</p>
<pre><code class="language-bash">curl -I https://my-server.com/path/to/font.woff</code></pre>
]]></content:encoded>
					
					<wfw:commentRss>https://jonathannicol.com/blog/2020/02/24/fixing-cors-font-errors-on-a-runcloud-server/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Loading missing images from a remote server</title>
		<link>https://jonathannicol.com/blog/2015/08/02/loading-missing-images-from-a-remote-server/</link>
					<comments>https://jonathannicol.com/blog/2015/08/02/loading-missing-images-from-a-remote-server/#comments</comments>
		
		<dc:creator><![CDATA[Jonathan]]></dc:creator>
		<pubDate>Mon, 03 Aug 2015 04:50:56 +0000</pubDate>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Wordpress]]></category>
		<guid isPermaLink="false">http://jonathannicol.com/?p=3282</guid>

					<description><![CDATA[When developing on a local server it can be painful to keep images in your media library synced with your production site. Sure, you could download the missing images and attachments to your local machine every time you need to work on the site, but this approach has two problems: It is time consuming The [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>When developing on a local server it can be painful to keep images in your media library synced with your production site. Sure, you could download the missing images and attachments to your local machine every time you need to work on the site, but this approach has two problems:</p>
<ol>
<li>It is time consuming</li>
<li>The files take up space on your local drive</li>
</ol>
<p>I recently discovered a way to seamlessly handle missing images so that they never 404 or occupy space on your local machine. The solution is an htaccess rewrite rule that checks if an image exists locally, and if it doesn&#8217;t, attempts to load it from your production server.</p>
<p>Here is a sample htaccess directive for WordPress:</p>
<pre><code class="language-bash"># Load media files from production server if they don't exist locally
&lt;IfModule mod_rewrite.c&gt;
  RewriteEngine on
  RewriteBase /
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{HTTP_HOST} ^localsite\.dev$
  RewriteRule ^wp-content/uploads/(.*)$ http://remotesite.com/wp-content/uploads/$1 [NC,L]
&lt;/IfModule&gt;</code></pre>
<p>Remember to change &#8220;remotesite.com&#8221; and &#8220;localsite.dev&#8221; in the example to match your own domain, and adjust your file paths as necessary. If you&#8217;re implementing this on a WordPress site make sure to place it before WordPress&#8217; own htaccess directive.</p>
<p>Hat tip to <a href="https://twitter.com/stevegrunwell">@stevegrunwell</a> for <a href="http://stevegrunwell.github.io/wordpress-git/#/13">bringing this technique to my attention</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://jonathannicol.com/blog/2015/08/02/loading-missing-images-from-a-remote-server/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Clean up pasted text in WordPress</title>
		<link>https://jonathannicol.com/blog/2015/02/19/clean-pasted-text-in-wordpress/</link>
					<comments>https://jonathannicol.com/blog/2015/02/19/clean-pasted-text-in-wordpress/#comments</comments>
		
		<dc:creator><![CDATA[Jonathan]]></dc:creator>
		<pubDate>Fri, 20 Feb 2015 06:06:41 +0000</pubDate>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Wordpress]]></category>
		<guid isPermaLink="false">http://jonathannicol.com/?p=3242</guid>

					<description><![CDATA[I have a love/hate relationship with the paste plugin in TinyMCE, the JavaScript WYSIWYG editor that ships with WordPress. In recent releases TinyMCE has gotten very good at sanitizing text pasted from MS Word, but it is still much more permissive than I would like. A long standing gripe is that classes are added to [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>I have a love/hate relationship with the paste plugin in <a href="http://www.tinymce.com/">TinyMCE</a>, the JavaScript WYSIWYG editor that ships with WordPress. In recent releases TinyMCE has gotten very good at sanitizing text pasted from MS Word, but it is still much more permissive than I would like.</p>
<p>A long standing gripe is that classes are added to every <code>paragraph</code> and <code>span</code> when I paste text into the editor:</p>
<pre><code class="language-markup">&lt;p class=&quot;p1&quot;&gt;This is &lt;span class=&quot;s1&quot;&gt;pasted&lt;/span&gt; text&lt;/p&gt;</code></pre>
<p>This odd behaviour is consistent across many applications &#8211; InDesign, TextEdit, Mail &#8211; so I suspect it may be occurring at an OS level. In any case, I don&#8217;t want class attributes finding their way into markup unless I put them there!</p>
<p>A more significant problem is that TinyMCE doesn&#8217;t filter out HTML tags from pasted content. If you copy and paste text from a website you might unknowingly bring its markup along for the ride, and end up with something like this:</p>
<pre><code class="language-markup">&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-8&quot;&gt;
    &lt;table&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;p&gt;This is pasted text&lt;/p&gt;&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/div&gt;</code></pre>
<p>I&#8217;m sure you can imagine how disastrous that extra markup could be on your website&#8217;s front end.</p>
<p>Unfortunately TinyMCE doesn&#8217;t have a configuration option to specify which tags are permitted in pasted markup. The <code><a href="http://www.tinymce.com/wiki.php/Configuration:valid_elements">valid_elements</a></code> option allows us to define which elements will remain in the edited text when TinyMCE saves, but that is an overzealous solution. On occasion you might need to add markup via TinyMCE, and <code>valid_elements</code> would strip it out again when you save your post. What is needed is a way to clean up text when it is pasted into TinyMCE, but still allow the user to type markup into the editor if they choose.</p>
<p>The solution comes in the form of the <code><a href="http://www.tinymce.com/wiki.php/Configuration:paste_postprocess">paste_preprocess</a></code> option, which allows us to specify a callback that will be executed when content is inserted into the editor. Within that callback we can strip out any tags that we don&#8217;t want, and remove <code>class</code>, <code>id</code> and any other undesirable attributes from our content.</p>
<p>In WordPress you can hook into TinyMCE&#8217;s configuration using the <code>tiny_mce_before_init</code> filter. In your theme&#8217;s <code>functions.php</code> file:</p>
<pre><code class="language-php">add_filter('tiny_mce_before_init','configure_tinymce');

/**
 * Customize TinyMCE's configuration
 *
 * @param   array
 * @return  array
 */
function configure_tinymce($in) {
  $in['paste_preprocess'] = &quot;function(plugin, args){
    // Strip all HTML tags except those we have whitelisted
    var whitelist = 'p,span,b,strong,i,em,h3,h4,h5,h6,ul,li,ol';
    var stripped = jQuery('&lt;div&gt;' + args.content + '&lt;/div&gt;');
    var els = stripped.find('*').not(whitelist);
    for (var i = els.length - 1; i &gt;= 0; i--) {
      var e = els[i];
      jQuery(e).replaceWith(e.innerHTML);
    }
    // Strip all class and id attributes
    stripped.find('*').removeAttr('id').removeAttr('class');
    // Return the clean HTML
    args.content = stripped.html();
  }&quot;;
  return $in;
}</code></pre>
<p>We use jQuery to populate the DOM with the pasted content, then traverse the DOM and remove tags and classes we don&#8217;t want. Finally we extract the cleaned HTML markup and pass it back to the editor. This approach may seem convoluted, but it is far less error prone than using regular expressions to sanitize our content.</p>
<p>All you need to do is modify the whitelist variable, which contains a list of tags to allow past your filter.</p>
<p>One last thing: You might notice that our entire JavaScript function is contained in a string. This might seem odd, but is necessary since WordPress stores TinyMCE&#8217;s configuration options as an array of strings.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://jonathannicol.com/blog/2015/02/19/clean-pasted-text-in-wordpress/feed/</wfw:commentRss>
			<slash:comments>13</slash:comments>
		
		
			</item>
		<item>
		<title>Particleground: a jQuery plugin for background particle systems</title>
		<link>https://jonathannicol.com/blog/2014/08/18/particleground-jquery-plugin-background-particle-systems/</link>
					<comments>https://jonathannicol.com/blog/2014/08/18/particleground-jquery-plugin-background-particle-systems/#comments</comments>
		
		<dc:creator><![CDATA[Jonathan]]></dc:creator>
		<pubDate>Mon, 18 Aug 2014 12:47:08 +0000</pubDate>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[News & Reviews]]></category>
		<category><![CDATA[Toolbox]]></category>
		<category><![CDATA[Web Design]]></category>
		<guid isPermaLink="false">http://jonathannicol.com/?p=3235</guid>

					<description><![CDATA[Lately I&#8217;ve noticed a few sites using JavaScript particle systems as animated backgrounds. I was curious about the technique so I created Particleground, a jQuery plugin that creates a similar effect. The particle system can be fine tuned using several configuration options, including a parallax effect which is controlled by the mouse on desktop computers [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Lately I&#8217;ve noticed a few sites using JavaScript particle systems as animated backgrounds. I was curious about the technique so I created <a href="https://jnicol.github.io/particleground" target="_blank">Particleground</a>, a jQuery plugin that creates a similar effect.</p>
<p class="post-img"><img loading="lazy" decoding="async" width="730" height="296" src="http://jonathannicol.com/cms/wp-content/uploads/2014/08/particleground.png" alt="particleground" class="alignnone size-full wp-image-3236" srcset="https://jonathannicol.com/cms/wp-content/uploads/2014/08/particleground.png 730w, https://jonathannicol.com/cms/wp-content/uploads/2014/08/particleground-300x121.png 300w" sizes="auto, (max-width: 730px) 100vw, 730px" /></p>
<p>The particle system can be fine tuned using several configuration options, including a parallax effect which is controlled by the mouse on desktop computers or the gyroscope on smart devices. Particleground works in any browser that supports HTML5 canvas.</p>
<p><a href="https://jnicol.github.io/particleground" target="_blank">See a demo</a></p>
<p><a href="https://github.com/jnicol/particleground/archive/master.zip">Download</a></p>
<p><a href="https://github.com/jnicol/particleground" target="_blank">View on Github</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://jonathannicol.com/blog/2014/08/18/particleground-jquery-plugin-background-particle-systems/feed/</wfw:commentRss>
			<slash:comments>9</slash:comments>
		
		
			</item>
		<item>
		<title>The Web Designer&#8217;s Bookshelf</title>
		<link>https://jonathannicol.com/blog/2014/07/07/web-designers-bookshelf/</link>
		
		<dc:creator><![CDATA[Jonathan]]></dc:creator>
		<pubDate>Tue, 08 Jul 2014 01:38:14 +0000</pubDate>
				<category><![CDATA[News & Reviews]]></category>
		<category><![CDATA[Web Design]]></category>
		<guid isPermaLink="false">http://jonathannicol.com/?p=3226</guid>

					<description><![CDATA[I have just launched a new personal project called The Web Designer&#8217;s Bookshelf, which is a curated archive of articles about web and interface design. Here&#8217;s the blurb: As an industry we are obsessed with technical implementation. We write endlessly about front-end processes or the latest CSS and JavaScript techniques, but by focussing so much [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>I have just launched a new personal project called <a href="http://webdesignersbookshelf.com/">The Web Designer&#8217;s Bookshelf</a>, which is a curated archive of articles about web and interface design.</p>
<p class="post-img"><img loading="lazy" decoding="async" width="730" height="252" src="http://jonathannicol.com/cms/wp-content/uploads/2014/07/wdb-header.jpg" alt="wdb-header" class="alignnone size-full wp-image-3231" srcset="https://jonathannicol.com/cms/wp-content/uploads/2014/07/wdb-header.jpg 730w, https://jonathannicol.com/cms/wp-content/uploads/2014/07/wdb-header-300x103.jpg 300w" sizes="auto, (max-width: 730px) 100vw, 730px" /></p>
<p>Here&#8217;s the blurb:</p>
<blockquote><p>As an industry we are obsessed with technical implementation. We write endlessly about front-end processes or the latest CSS and JavaScript techniques, but by focussing so much of our attention on technical concerns we have squeezed design issues out of the conversation.</p>
<p>The Web Designer’s Bookshelf is my attempt to rectify this imbalance. The site is an archive of articles that examine the history and craft of web and interface design, and related design disciplines, in a critical and thought provoking fashion.</p>
<p>Unlike technical articles, which date quickly, writing about design principles tends to have a longer shelf life. My hope is that the articles collected here will be of lasting interest to designers of all persuasions, and contribute to an ongoing conversation about the interactive design discipline.</p></blockquote>
<p>Visit <a href="http://webdesignersbookshelf.com/">The Web Designer&#8217;s Bookshelf website</a>, subscribe to the newsletter or follow <a href="https://twitter.com/webdesbookshelf">@webdesbookshelf</a> on Twitter. </p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>A super minimal functions.php file</title>
		<link>https://jonathannicol.com/blog/2014/06/22/super-minimal-functions-php-file/</link>
					<comments>https://jonathannicol.com/blog/2014/06/22/super-minimal-functions-php-file/#comments</comments>
		
		<dc:creator><![CDATA[Jonathan]]></dc:creator>
		<pubDate>Mon, 23 Jun 2014 05:38:24 +0000</pubDate>
				<category><![CDATA[Wordpress]]></category>
		<guid isPermaLink="false">http://jonathannicol.com/?p=3139</guid>

					<description><![CDATA[In my last post I talked about strategies to organize your WordPress theme&#8217;s functions.php file by following object-oriented programming principles. If you prefer to stick with a procedural coding style, here is a simple way to keep your functions.php file lean and mean. Simply save each hooked function in a separate file, then use PHP [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>In my last post I talked about strategies to organize your WordPress theme&#8217;s <code>functions.php</code> file by following object-oriented programming principles. If you prefer to stick with a procedural coding style, here is a simple way to keep your <code>functions.php</code> file lean and mean.</p>
<p>Simply save each hooked function in a separate file, then use PHP includes to import them into <code>functions.php</code>:</p>
<pre><code class="language-php">require_once('includes/custom_excerpt_length.php');
require_once('includes/another_function.php');
require_once('includes/a_third_function.php');
// etc.</code></pre>
<p>It doesn&#8217;t get much more minimal than that!</p>
<p>In this example our <code>custom_excerpt_length.php</code> file might look like this:</p>
<pre><code class="language-php">function custom_excerpt_length () {
  return 10;
}
add_filter('excerpt_length', 'custom_excerpt_length');</code></pre>
]]></content:encoded>
					
					<wfw:commentRss>https://jonathannicol.com/blog/2014/06/22/super-minimal-functions-php-file/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Organize your WordPress functions.php file</title>
		<link>https://jonathannicol.com/blog/2014/06/22/organize-wordpress-functions-file/</link>
					<comments>https://jonathannicol.com/blog/2014/06/22/organize-wordpress-functions-file/#comments</comments>
		
		<dc:creator><![CDATA[Jonathan]]></dc:creator>
		<pubDate>Mon, 23 Jun 2014 05:32:30 +0000</pubDate>
				<category><![CDATA[Wordpress]]></category>
		<guid isPermaLink="false">http://jonathannicol.com/?p=3108</guid>

					<description><![CDATA[WordPress tutorials will often advise you to copy and paste code snippets directly into your theme&#8217;s functions.php file. That might seem harmless enough, but if you keep adding functionality in an arbitrary fashion your code can quickly become unmanageable. In this post I will share some strategies for keeping your functions.php file tidy and making [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>WordPress tutorials will often advise you to copy and paste code snippets directly into your theme&#8217;s <code>functions.php</code> file. That might seem harmless enough, but if you keep adding functionality in an arbitrary fashion your code can quickly become unmanageable. In this post I will share some strategies for keeping your <code>functions.php</code> file tidy and making it easier to maintain.</p>
<h2>Keep the global space clean</h2>
<p>One problem with the typical <code>functions.php</code> file &#8211; and, it could be argued, with WordPress generally &#8211; is that variables and functions are declared in the global scope. That means you risk naming conflicts with functions and variables declared elsewhere in your own code, in plugins, or in the WordPress core.</p>
<p>As an example, suppose you create a function named <code>get_post_slug</code> in <code>functions.php</code>. Now let&#8217;s imagine that in the future a function with the same name is introduced to the WordPress core. Your function declaration will now throw a fatal error:</p>
<pre><code class="language-none">Fatal error: Cannot redeclare get_post_slug()</code></pre>
<p>To avoid naming collisions you could prefix your function names to make them unique &#8211; for example <code>yourthemename_get_post_slug</code> &#8211; but a more robust approach is to encapsulate your theme&#8217;s logic in a class.</p>
<p>A typical hooked function looks like this:</p>
<pre><code class="language-php">function custom_excerpt_length () {
  return 100;
}

add_filter('excerpt_length', 'custom_excerpt_length');</code></pre>
<p>Here is that same functionality encapsulated in a class:</p>
<pre><code class="language-php">class YourThemeName {
  function __construct() {
    add_filter('excerpt_length', array($this, 'custom_excerpt_length'));
  }

  function custom_excerpt_length () {
    return 100;
  }
}

$yourthemename = new YourThemeName();</code></pre>
<p>Because the <code>custom_excerpt_length</code> function is a class method it no longer has global scope, and the <code>$yourthemename</code> variable ensures that our theme&#8217;s logic has a single point of access. (Note: You should change <code>YourThemeName</code> and <code>$yourthemename</code> to something unique to your theme.)</p>
<p>You might have noticed that the second parameter passed to <code>add_filter</code> is an array, with <code>$this</code> as the first value. This tells WordPress to use a class method as a callback. You can read more about the different types of callbacks in the <a href="http://codex.wordpress.org/Function_Reference/add_filter"><code>add_filter</code> documentation</a>.</p>
<h2>Theme setup</h2>
<p>Most themes have some setup code that needs to run straight away. Let&#8217;s add an <code>init</code> method to our class to handle this job:</p>
<pre><code class="language-php">class YourThemeName {
  function __construct() {
    add_action('after_setup_theme', array($this, 'init'));
    // Register action/filter callbacks here...
  }

  function init() {
    // All theme initialization code goes here...
    register_nav_menus(
      array(
        'header-menu' => __('Header Menu', 'yourthemename')
      )
    );
  }

  // Action/filter callbacks go here...
}

$yourthemename = new YourThemeName();</code></pre>
<p>Any functionality you want hooked to the <code>after_setup_theme</code> action can go in the <code>init</code> method. In the example above we registered a navigation menu.</p>
<h2>Utility methods</h2>
<p>In addition to WordPress action/filter hooks your theme class can also provide utility methods, which can be accessed from templates.</p>
<p>Here is a simple utility method to get the ID of a category by supplying its name:</p>
<pre><code class="language-php">function get_category_id ($cat_name) {
  $term = get_term_by('name', $cat_name, 'category');
  return $term->term_id;
}</code></pre>
<p>If you include that method in your theme&#8217;s class you can now access it from a template like so:</p>
<pre><code class="language-php">$yourthemename->get_category_id('category-name');</code></pre>
<h2>Group your functions by type</h2>
<p>Rather than arranging callback function in a haphazard fashion, grouping them by type makes them easier to find.</p>
<p>As we saw earlier all of your theme&#8217;s filter/action callbacks are registered in the class constructor, and all setup logic is placed within the <code>init</code> method. I also like to group all my action callback functions together, my filter callback functions together and my utility methods together.</p>
<pre><code class="language-php">class YourThemeName {
  function __construct() {
    add_action('after_setup_theme', array($this, 'init'));
    // Register action/filter callbacks here...
  }

  function init() {
    // Theme setup code goes here...
  }

  // Action callbacks go here...

  // Filter callbacks go here...

  // Utility methods go here...
}</code></pre>
<h2>Comment your code</h2>
<p>It might go without saying, but well written comments make your code easier to read and comprehend. Well structured comments can go a long way toward organizing your <code>functions.php</code> file.</p>
<pre><code class="language-php">/**
 * Get the category id from a category name
 *
 * @param string $cat_name The category name
 * @return int The category ID
 */
function get_category_id ($cat_name) {
  $term = get_term_by('name', $cat_name, 'category');
  return $term->term_id;
}</code></pre>
<h2>Use a plugin for site-specific functionality</h2>
<p>While it may be tempting to dump all your site&#8217;s custom functions into your <code>functions.php</code> file, it is good practice to encapsulate site-specific logic in a separate functionality plugin. In addition to long term maintenance benefits, this separation of concerns streamlines your <code>functions.php</code> file by offloading some of its functionality to a plugin.</p>
<p>There have been <a href="http://wpcandy.com/teaches/how-to-create-a-functionality-plugin/">many good articles</a> written about what belongs in a <code>functions.php</code> file and what belongs in a functionality plugin, but I think Curtis McHale <a href="http://wpthemetutorial.com/2012/03/20/where-does-my-code-go-functions-php-or-a-plugin/">sums it up nicely</a>:</p>
<blockquote><p>The only deciding factor in where you put your code is the life of the functionality. Will you still need to have that portfolio custom post type the next time you redesign your site? If the answer is yes then you should be building a plugin with the code. If the answer is no, then put the code in you theme <code>functions.php</code>.</p></blockquote>
<h2>Example functions.php file</h2>
<p>If you employ the strategies I have outlined in this post I hope you will find that your <code>functions.php</code> file is better organized and easier to manage. Here is an example of how the final file might look:</p>
<pre><code class="language-php">/**
 * YourThemeName
 */
class YourThemeName {
  /**
   * Constructor
   */
  function __construct() {
    // Register action/filter callbacks
    add_action('after_setup_theme', array($this, 'init'));
    add_filter('excerpt_length', array($this, 'custom_excerpt_length'));
  }

  /**
   * Theme setup
   */
  function init() {
    // Register navigation menus
    register_nav_menus(
      array(
        'header-menu' => __('Header Menu', 'yourthemename')
      )
    );
  }

  /**
   * Filter callbacks
   * ----------------
   */

  /**
   * Customize post excerpt length
   *
   * @return int The new excerpt length in words
   */
  function custom_excerpt_length () {
    return 100;
  }

  /**
   * Utility methods
   * ---------------
   */

  /**
   * Get the category id from a category name
   *
   * @param string $cat_name The category name
   * @return int The category ID
   */
  function get_category_id ($cat_name) {
    $term = get_term_by('name', $cat_name, 'category');
    return $term->term_id;
  }
}

// Instantiate theme
$yourthemename = new YourThemeName();</code></pre>
]]></content:encoded>
					
					<wfw:commentRss>https://jonathannicol.com/blog/2014/06/22/organize-wordpress-functions-file/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title>Center and crop thumbnails with CSS</title>
		<link>https://jonathannicol.com/blog/2014/06/16/centre-crop-thumbnails-with-css/</link>
					<comments>https://jonathannicol.com/blog/2014/06/16/centre-crop-thumbnails-with-css/#comments</comments>
		
		<dc:creator><![CDATA[Jonathan]]></dc:creator>
		<pubDate>Tue, 17 Jun 2014 02:55:10 +0000</pubDate>
				<category><![CDATA[CSS]]></category>
		<guid isPermaLink="false">http://jonathannicol.com/?p=3071</guid>

					<description><![CDATA[Here is a handy CSS centering technique I first noticed in the WordPress media library, where it is used to centre and crop irregularly sized thumbnails within a square container. The technique uses CSS3 transforms, so it works in all modern browsers, including IE9 and above. &#60;div class=&#34;thumbnail&#34;&#62; &#60;img src=&#34;landscape-img.jpg&#34; alt=&#34;Image&#34; /&#62; &#60;/div&#62; &#60;div class=&#34;thumbnail&#34;&#62; [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Here is a handy CSS centering technique I first noticed in the WordPress media library, where it is used to centre and crop irregularly sized thumbnails within a square container.</p>
<p class="post-img"><img loading="lazy" decoding="async" width="394" height="638" src="http://jonathannicol.com/cms/wp-content/uploads/2014/06/cropped-thumbnails.png" alt="cropped-thumbnails" class="alignnone size-full wp-image-3086" srcset="https://jonathannicol.com/cms/wp-content/uploads/2014/06/cropped-thumbnails.png 394w, https://jonathannicol.com/cms/wp-content/uploads/2014/06/cropped-thumbnails-185x300.png 185w" sizes="auto, (max-width: 394px) 100vw, 394px" /></p>
<p>The technique uses CSS3 transforms, so it works in all modern browsers, including IE9 and above.</p>
<pre><code class="language-markup">&lt;div class=&quot;thumbnail&quot;&gt;
  &lt;img src=&quot;landscape-img.jpg&quot; alt=&quot;Image&quot; /&gt;
&lt;/div&gt;
&lt;div class=&quot;thumbnail&quot;&gt;
  &lt;img src=&quot;portrait-img.jpg&quot; class=&quot;portrait&quot; alt=&quot;Image&quot; /&gt;
&lt;/div&gt;</code></pre>
<pre><code class="language-css">.thumbnail {
  position: relative;
  width: 200px;
  height: 200px;
  overflow: hidden;
}
.thumbnail img {
  position: absolute;
  left: 50%;
  top: 50%;
  height: 100%;
  width: auto;
  -webkit-transform: translate(-50%,-50%);
      -ms-transform: translate(-50%,-50%);
          transform: translate(-50%,-50%);
}
.thumbnail img.portrait {
  width: 100%;
  height: auto;
}</code></pre>
<p>The technique works by positioning the image so that its top left corner is in the centre of its container. Then, a 2D translation moves it up and left by half its own width.</p>
<p>The key here is that the percentage values passed to the <code>translate</code> function are relative to the element, not its container, as would be the case if we were to manipulate the element’s <code>top</code> and <code>left</code> properties instead.</p>
<p><a href="http://jonathannicol.com/projects/center-and-crop-thumbnail/">View a demo</a></p>
<p>Note that the portrait format image has the class <code>portrait</code>, so that we can correctly scale it to fill its container.</p>
<p>So there you go. A really simple CSS technique for centering and cropping thumbnails.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://jonathannicol.com/blog/2014/06/16/centre-crop-thumbnails-with-css/feed/</wfw:commentRss>
			<slash:comments>48</slash:comments>
		
		
			</item>
	</channel>
</rss>
