I Spun up a Scalable WordPress Server Environment with Trellis, and You Can, Too

A few years back, my fledgling website design agency was starting to take shape; however, we had one problem: managing clients’ web servers and code deployments. We were unable to build a streamlined process of provisioning servers and maintaining operating system security patches. We had the development cycle down pat, but server management became the bane of our work. We also needed tight control over each server depending on a site’s specific needs. Also, no, shared hosting was not the long term solution.

I began looking for a prebuilt solution that could solve this problem but came up with no particular success. At first, I manually provisioned servers. This process quickly proved to be both monotonous and prone to errors. I eventually learned Ansible and created a homegrown conglomeration of custom Ansible roles, bash scripts and Ansible Galaxy roles that further simplified the process — but, again, there were still many manual steps needed to take before the server was 100%.

I’m not a server guy (nor do I pretend to be one), and at this point, it became apparent that going down this path was not going to end well in the long run. I was taking on new clients and needed a solution, or else I would risk our ability to be sustainable, let alone grow. I was spending gobs of time typing arbitrary sudo apt-get update commands into a shell when I should have been managing clients or writing code. That’s not to mention I was also handling ongoing security updates for the underlying operating system and its applications.

Tell me if any of this sounds familiar.

Serendipitously, at this time, the team at Roots had released Trellis for server provisioning; after testing it out, things seemed to fall into place. A bonus is that Trellis also handles complex code deployments, which turned out to be something else I needed as most of the client sites and web applications that we built have a relatively sophisticated build process for WordPress using Composer, npm, webpack, and more. Better yet, it takes just minutes to jumpstart a new project. After spending hundreds of hours perfecting my provisioning process with Trellis, I hope to pass what I’ve learned onto you and save you all the hours of research, trials, and manual work that I wound up spending.

A note about Bedrock

We’re going to assume that your WordPress project is using Bedrock as its foundation. Bedrock is maintained by the same folks who maintain Trellis and is a “WordPress boilerplate with modern development tools, easier configuration, and an improved folder structure.” This post does not explicitly explain how to manage Bedrock, but it is pretty simple to set up, which you can read about in its documentation. Trellis is natively designed to deploy Bedrock projects.

A note about what should go into the repo of a WordPress site

One thing that this entire project has taught me is that WordPress applications are typically just the theme (or the child theme in the parent/child theme relationship). Everything else, including plugins, libraries, parent themes and even WordPress itself are just dependencies. That means that our version control systems should typically include the theme alone and that we can use Composer to manage all of the dependencies. In short, any code that is managed elsewhere should never be versioned. We should have a way for Composer to pull it in during the deployment process. Trellis gives us a simple and straightforward way to accomplish this.

Getting started

Here are some things I’m assuming going forward:

  • The code for the new site in the directory ~/Sites/newsite
  • The staging URL is going to be https://newsite.statenweb.com
  • The production URL is going to be https://newsite.com
  • Bedrock serves as the foundation for your WordPress application
  • Git is used for version control and GitHub is used for storing code. The repository for the site is: git@github.com:statenweb/newsite.git

I am a little old school in my local development environment, so I’m foregoing Vagrant for local development in favor of MAMP. We won’t go over setting up the local environment in this article.

I set up a quick start bash script for MacOS to automate this even further.

The two main projects we are going to need are Trellis and Bedrock. If you haven’t done so already, create a directory for the site (mkdir ~/Sites/newsite) and clone both projects from there. I clone Trellis into a /trellis directory and Bedrock into the /site directory:

cd ~/Sites/newsite
git clone git@github.com:roots/trellis.git
git clone git@github.com:roots/bedrock.git site
cd trellis
rm -rf .git
cd ../site
rm -rf .git

The last four lines enable us to version everything correctly. When you version your project, the repo should contain everything in ~/Sites/newsite.

Now, go into trellis and make the following changes:

First, open ~/Sites/newsite/trellis/ansible.cfg and add these lines to the bottom of the [defaults] key:

vault_password_file = .vault_pass
host_key_checking = False

The first line allows us to use a .vault_pass file to encrypt all of our vault.yml files which are going to store our passwords, sensitive data, and salts.

The second host_key_checking = False can be omitted for security as it could be considered somewhat dangerous. That said, it’s still helpful in that we do not have to manage host key checking (i.e., typing yes when prompted).

Ansible vault password

Photo by Micah Williams on Unsplash

Next, let’s create the file ~/Sites/newsite/trellis/.vault_pass and enter a random hash of 64 characters in it. We can use a hash generator to create that (see here for example). This file is explicitly ignored in the default .gitignore, so it will (or should!) not make it up to the source control. I save this password somewhere extremely secure. Be sure to run chmod 600 .vault_pass to restrict access to this file.

The reason we do this is so we can store encrypted passwords in the version control system and not have to worry about exposing any of the server’s secrets. The main thing to call out is that the .vault_pass file is (and should not be) not committed to the repo and that the vault.yml file is properly encrypted; more on this in the “Encrypting the Secret Variables” section below.

Setting up target hosts

Photo by N. on Unsplash

Next, we need to set up our target hosts. The target host is the web address where Trellis will deploy our code. For this tutorial, we are going to be configuring newsite.com as our production target host and newsite.statenweb.com as our staging target host. To do this, let’s first update the production servers address in the production host file, stored in ~/Sites/newsite/trellis/hosts/production to:

newsite.com [web]

Next, we can update the staging server address in the staging host file, which is stored in ~/Sites/newsite/trellis/hosts/staging to:

newsite.statenweb.com [web]

Setting up GitHub SSH Keys

For deployments to be successful, SSH keys need to be working. Trellis takes advantage of how GitHub’ exposes all public (SSH) keys so that you do not need to add keys manually. To set this up go into the group_vars/all/users.yml and update both the web_user and the admin_user object’s keys value to include your GitHub username. For example:

users: - name: '{{ web_user }}' groups: - '{{ web_group }}' keys: - https://github.com/matgargano.keys - name: '{{ admin_user }}' groups: - sudo keys: - https://github.com/matgargano.keys

Of course, all of this assumes that you have a GitHub account with all of your necessary public keys associated with it.

Site Meta

We store essential site information in:

  • ~/Sites/newsite/trellis/group_vars/production/wordpress_sites.yml for production
  • ~/Sites/newsite/trellis/group_vars/staging/wordpress_sites.yml for staging.

Let’s update the following information for our staging wordpress_sites.yml:

wordpress_sites: newsite.statenweb.com: site_hosts: - canonical: newsite.statenweb.com local_path: ../site repo: git@github.com:statenweb/newsite.git repo_subtree_path: site branch: staging multisite: enabled: false ssl: enabled: true provider: letsencrypt cache: enabled: false

This file is saying that we:

  • removed the site hosts redirects as they are not needed for staging
  • set the canonical site URL (newsite.statenweb.com) for the site key (newsite.statenweb.com)
  • defined the URL for the repository
  • the git repo branch that gets deployed to this target is staging, i.e., we are using a separate branch named staging for our staging site
  • enabled SSL (set to true), which will also install an SSL certificate when the box provisions

Let’s update the following information for our production wordpress_sites.yml:

wordpress_sites: newsite.com: site_hosts: - canonical: newsite.com redirects: - www.newsite.com local_path: ../site # path targeting local Bedrock site directory (relative to Ansible root) repo: git@github.com:statenweb/newsite.git repo_subtree_path: site branch: master multisite: enabled: false ssl: enabled: true provider: letsencrypt cache: enabled: false

Again, what this translates to is that we:

  • set the canonical site URL (newsite.com) for the site key (newsite.com)
  • set a redirect for www.newsite.com
  • defined the URL for the repository
  • the git repo branch that gets deployed to this target is master, i.e., we are using a separate branch named master for our production site
  • enabled SSL (set to true), which will install an SSL certificate when you provision the box

In the wordpress_sites.yml you can further configure your server with caching, which is beyond the scope of this guide. See Trellis’ documentation on FastCGI Caching for more information.

Secret Variables

Photo by Kristina Flour on Unsplash

There are going to be several secret pieces of information for both our staging and production site including the root user password, MySQL root password, site salts, and more. As referenced previously, Ansible Vault and using .vault_pass file makes this a breeze.

We store this secret site information in:

  • ~/Sites/newsite/trellis/group_vars/production/vault.yml for production
  • ~/Sites/newsite/trellis/group_vars/staging/vault.yml for staging

Let’s update the following information for our staging vault.yml:

vault_mysql_root_password: pK3ygadfPHcLCAVHWMX vault_users: - name: "{{ admin_user }}" password: QvtZ7tdasdfzUmJxWr8DCs salt: "heFijJasdfQbN8bA3A" vault_wordpress_sites: newsite.statenweb.com: env: auth_key: "Ab$YTlX%:Qt8ij/99LUadfl1:U]m0ds@N<3@x0LHawBsO$(gdrJQm]@alkr@/sUo.O" secure_auth_key: "+>Pbsd:|aiadf50;1Gz;.Z{nt%Qvx.5m0]4n:L:h9AaexLR{1B6.HeMH[w4$>H_" logged_in_key: "c3]7HixBkSC%}-fadsfK0yq{HF)D#1S@Rsa`i5aW^jW+W`8`e=&PABU(s&JH5oPE" nonce_key: "5$vig.yGqWl3G-.^yXD5.ddf/BsHx|i]>h=mSy;99ex*Saj<@lh;3)85D;#|RC=" auth_salt: "Wv)[t.xcPsA}&/]rhxldafM;h(FSmvR]+D9gN9c6{*hFiZ{]{,#b%4Um.QzAW+aLz" secure_auth_salt: "e4dz}_x)DDg(si/8Ye&U.p@pB}NzHdfQccJSAh;?W)>JZ=8:,i?;j$bwSG)L!JIG" logged_in_salt: "DET>c?m1uMAt%hj3`8%_emsz}EDM7R@44c0HpAK(pSnRuzJ*WTQzWnCFTcp;,:44" nonce_salt: "oHB]MD%RBla*#x>[UhoE{hm{7j#0MaRA#fdQcdfKe]Y#M0kQ0F/0xe{cb|g,h.-m"

Now, let’s update the following information for our production vault.yml:

vault_mysql_root_password: nzUMN4zBoMZXJDJis3WC
vault_users: - name: "{{ admin_user }}" password: tFxea6ULFM8CBejagwiU salt: "9LgzE8phVmNdrdtMDdvR"
vault_wordpress_sites: newsite.com: env: db_password: eFKYefM4hafxCFy3cash # Generate your keys here: https://roots.io/salts.html auth_key: "|4xA-:Pa=-rT]&!-(%*uKAcd</+m>ix_Uv,`/(7dk1+;b|ql]42gh&HPFdDZ@&of" secure_auth_key: "171KFFX1ztl+1I/P$bJrxi*s;}.>S:{^-=@*2LN9UfalAFX2Nx1/Q&i&LIrI(BQ[" logged_in_key: "5)F+gFFe}}0;2G:k/S>CI2M*rjCD-mFX?Pw!1o.@>;?85JGu}#(0#)^l}&/W;K&D" nonce_key: "5/[Zf[yXFFgsc#`4r[kGgduxVfbn::<+F<$jw!WX,lAi41#D-Dsaho@PVUe=8@iH" auth_salt: "388p$c=GFFq&hw6zj+T(rJro|V@S2To&dD|Q9J`wqdWM&j8.KN]y?WZZj$T-PTBa" secure_auth_salt: "%Rp09[iM0.n[ozB(t;0vk55QDFuMp1-=+F=f%/Xv&7`_oPur1ma%TytFFy[RTI,j" logged_in_salt: "dOcGR-m:%4NpEeSj>?A8%x50(d0=[cvV!2x`.vB|^#G!_D-4Q>.+1K!6FFw8Da7G" nonce_salt: "rRIHVyNKD{LQb$uOhZLhz5QX}P)QUUo!Yw]+@!u7WB:INFFYI|Ta5@G,j(-]F.@4"

The essential lines for both are that:

  • The site key must match the key in wordpress_sites.yml we are using newsite.statenweb.com: for staging and newsite.com: for production
  • I randomly generated vault_mysql_root_password, password, salt, db_password, and db_password. I used Roots’ helper to generate the salts.

I typically use Gmail’s SMTP servers using the Post SMTP plugin, so there’s no need for me to edit the ~/Sites/newsite/group_vars/all/vault.yml.

Encrypting the Secret Variables

Photo by Markus Spiske on Unsplash

As previously mentioned we use Ansible Vault to encrypt our vault.yml files. Here’s how to encrypt the files and make them ready to be stored in our version control system:

cd ~/Sites/newsite/trellis
ansible-vault encrypt group_vars/staging/vault.yml group_vars/production/vault.yml

Now, if we open either ~/Sites/newsite/trellis/group_vars/staging/vault.yml or ~/Sites/newsite/trellis/group_vars/production/vault.yml, all we’ll get is garbled text. This is safe to be stored in a repository as the only way to decrypt it is to use the .vault_pass. It goes without saying to make extra sure that the .vault_pass itself does not get committed to the repository.

A note about compiling, transpiling, etc.

Another thing that’s out of scope is setting up Trellis deployments to handle a build process using build tools such as npm and webpack. This is example code to handle a custom build that could be included in ~/Sites/newsite/trellis/deploy-hooks/build-before.yml:

--- - args: chdir: "{{ project.local_path }}/web/app/themes/newsite" command: "npm install" connection: local name: "Run npm install" - args: chdir: "{{ project.local_path }}/web/app/themes/newsite" command: "npm run build" connection: local name: "Compile assets for production" - name: "Copy Assets" synchronize: dest: "{{ deploy_helper.new_release_path }}/web/app/themes/newsite/dist/" group: no owner: no rsync_opts: "--chmod=Du=rwx,--chmod=Dg=rx,--chmod=Do=rx,--chmod=Fu=rw,--chmod=Fg=r,--chmod=Fo=r" src: "{{ project.local_path }}/web/app/themes/newsite/dist/"

These are instructions that build assets and moves them into a directory that I explicitly decided not to version. I hope to write a follow-up guide that dives specifically into that.


Photo by Bill Jelen on Unsplash

I am not going to go in great detail about setting up the servers themselves, but I typically would go into DigitalOcean and spin up a new droplet. As of this writing, Trellis is written on Ubuntu 18.04 LTS (Bionic Beaver) which acts as the production server. In that droplet, I would add a public key that is also included in my GitHub account. For simplicity, I can use the same server as my staging server. This scenario is likely not what you would be using; maybe you use a single server for all of your staging sites. If that is the case, then you may want to pay attention to the passwords configured in ~/Sites/newsite/trellis/group_vars/staging/vault.yml.

At the DNS level, I would map the naked A record for newsite.com to the IP address of the newly created droplet. Then I’d map the CNAME www to @. Additionally, the A record for newsite.statenweb.com would be mapped to the IP address of the droplet (or, alternately, a CNAME record could be created for newsite.statenweb.com to newsite.com since they are both on the same box in this example).

After the DNS propagates, which can take some time, the staging box can be provisioned by running the following commands.

First off, it;’s possible you may need to run this before anything else:

ansible-galaxy install -r requirements.yml

Then, install the required Ansible Galaxy roles before proceeding:

cd ~/Sites/newsite/trellis
ansible-playbook server.yml -e env=staging

Next up, provision the production box:

cd ~/Sites/newsite/trellis
ansible-playbook server.yml -e env=production


If all is set up correctly to deploy to staging, we can run these commands:

cd ~/Sites/newsite/trellis
ansible-playbook deploy.yml -e "site=newsite.statenweb.com env=staging" -i hosts/staging

And, once this is complete, hit https://newsite.statenweb.com. That should bring up the WordPress installation prompt that provides the next steps to complete the site setup.

If staging is good to go, then we can issue the following commands to deploy to production:

cd ~/Sites/newsite/trellis
ansible-playbook deploy.yml -e "site=newsite.com env=production" -i hosts/production

And, like staging, this should also prompt installation steps to complete when hitting https://newsite.com.

Go forth and deploy!

Hopefully, this gives you an answer to a question I had to wrestle with personally and saves you a ton of time and headache in the process. Having stable, secure and scalable server environments that take relatively little effort to spin up has made a world of difference in the way our team works and how we’re able to accommodate our clients’ needs.

While we’re technically done at this point, there are still further steps to take to wrap up your environment fully:

  • Add dependencies like plugins, libraries and parent themes to ~/Sites/newsite/composer.json and run composer update to grab the latest manifest versions.
  • Place the theme to ~/Sites/newsite/site/themes/. (Note that any WordPress theme can be used.)
  • Include any build processes you’d need (e.g. transpiling ES6, compiling SCSS, etc.) in one of the deployment hooks. (See the documentation for Trellis Hooks).

I have also been able to dive into enterprise-level continuous integration and continuous delivery, as well as how to handle premium plugins with Composer by running a custom Composer server, among other things, while incurring no additional cost. Hopefully, those are areas I can touch on in future posts.

Trellis provides a dead simple way to provision WordPress servers. Thanks to Trellis, long gone are the days of manually creating, patching and maintaining servers!

The post I Spun up a Scalable WordPress Server Environment with Trellis, and You Can, Too appeared first on CSS-Tricks.

Renaissance Metal

Many of the first printed books in Europe were decorated with illustrations, initials and borders. Each served a purpose: initials signaled, via their range of sizes, a textual hierarchy, working in much the same way as chapter headings and sub-headings do today. Decorative borders were employed to demarcate or divide books, chapters or sections and, […]

The post Renaissance Metal appeared first on I Love Typography.

10 Real-World Reasons Designers Should Know SEO

For web designers today, creating a website can mean a whole lot than just functionality, usability and aesthetic appeal. Today, every new-born website requires a thorough integration of Search Engine Optimization (SEO) protocols to become crawlable and get indexed by search engines such as Google.

A good website can attract great amounts of traffic. However, to make sure your traffic is relevant, geo-specific, and hails from the target segment, you must utilize SEO properly. According to one piece of HubSpot research, 77% of people research a brand before getting in touch with it. This means your site design, structure, content, and marketing practices must be spot on if you want spectacular search results!

Both off-page and on-page SEO are imperative to the ranking process for any website on Google. Here, we are going to discuss why web designers should know about on-page SEO well enough to create a website that not only attracts visitors, but also ranks on top of Google search engine result pages (SERPs).

1. Higher Rankings

On-page SEO involves many elements such as HTTP status code, URLs and their friendliness with the search engine. Other aspects include the correct addition of meta tags, descriptions and further heading tags on your search link on Google SERPs. All of these elements make a huge difference in on-page SEO. Therefore, a web designer who knows these details must know when to apply them in the right order so that the website receives higher rankings on Google.

2. Greater Search Accuracy

With the growing number of internet users, the demand of the data has also increased. There are so many brands for a similar product, over hundreds of online stores, and numerous branches of the same brand. Before any potential customer makes an appearance in a store, they are highly likely to search them on the internet. The statistics clearly support this as 18% more shoppers prefer Google over Amazon for searching a product and 136% of times a search engine is preferred over other websites for the same purpose. Similarly, local searches lead 50% of the mobile users to take a tour to the nearby store within 24 hours. This further necessitates web designers to readily know about on-page SEO so that the client’s business page is more visible on web.

3. More Mobile Traffic

The state of inbound reporting suggests that generating traffic is one of the main marketing challenges faced by website designers and marketers. Website designers have the opportunity to integrate SEO metrics from the start and not only make the website more user-friendly, but device responsive as well. According to marketing technology facts by Sweor 57% of the mobile users abandon a brand’s website if it has a poor mobile responsive website. SEO helps you improve these flaws and add in high-quality visual content for better marketing. Designers can use this to their advantage and focus on building an attractive, rankable and responsive website.

4. Higher Engagement

In the present era, every online brand is reflection of how far up it is on Google rankings. On-page SEO helps build a strong network of internal linking that keeps the user engaged on the website by offering them more valuable information on the right time.

It also helps brings exposure to those sectors of the website that need more attention and helps generate a positive user experience from the visitor. This helps the brand focus on its goals and deploy different marketing strategies to boost revenues.

5. Impartial Benefits for SMEs

While large businesses may dominate the small ones in terms of size, operations and employee strength, SEO does not discriminate between SMEs and Large enterprises. SEO does not require a sizeable investment and most entrepreneurs and SMEs can afford hiring a few resources or even build their own department. However, SMEs with constrained budgets may not be able afford a dedicated department for SEO. Therefore, web designers must know SEO beforehand since there is no guarantee they will get any guidance from the company when the website gets live.

6. More Quality Traffic

Designing a website with proper on-page SEO helps Google’s spiders to crawl through your URLs faster and index your pages more relevantly on their SERPs. Research conducted by Moz suggests that 71.33% of clicks made on a website are present on the first page of search results. This means that more and quality traffic would be driven to your website generating more leads, increasing the conversion rates and ROI as well.

7. Using Innovative Technologies

Content has a direct effect on your customers. According MindMeld, 60% of the users have started using voice search features to interact with search engines when making queries. This means that the designers now need to optimize the website and content for voice search as well. According to Backlinko, the average word length that helps rank the website in the first page of Google is 1890 words. Also, the use of most suitable keywords gives your website ranking a boost bringing it on the first result page of the search engine. To get more advanced SEO features, web designers also deploy SEO extensions for more optimized performance and cost effectiveness.

8. Increases Page Loading Speed

Every website designer knows that loading speed plays a deciding role in online rankings as well as user experience. Some of the factors that lower the webpage speed are the large images, bad URLs and coding, and themes with too many widgets. Thus, knowing on-page SEO helps the designer avoid such errors when designing the website, improving its loading speeds far more efficiently as compared to when it is operational.

9. Greater User Experience

You must be wondering how SEO improves the UX, right? Well, good SEO offers informative, readable and highly usable content to the readers. Also, it helps to design a visually attractive website that is nicely navigated and performs well. These features make users happy and enhance their experience on the web page. So if you’re planning to leave a long lasting impression right from the start, you must put in some on-page SEO from the beginning.

10. Cost-Effectiveness

Its irrefutable that SEO has a great cost advantage. A skilled web designer knows how well systematic integration of on-page SEO can save costs that can pile up later if the website starts getting traffic. Everything from page titles, meta descriptions, meta tags, URL structure, body tags, keyword density down to image SEO must be prepared prior to its operation stage. Neglecting these key points can be detrimental to the website’s overall progress and may result on expensive retro-fitting at a later date.


Featured image via Unsplash

Add Realistic Chalk and Sketch Lettering Effects with Sketch’it – only $5!

p img {display:inline-block; margin-right:10px;}
.alignleft {float:left;}
p.showcase {clear:both;}
body#browserfriendly p, body#podcast p, div#emailbody p{margin:0;}

Fresh Spring Vibes For Your Desktop (March 2019 Wallpapers Edition)

Fresh Spring Vibes For Your Desktop (March 2019 Wallpapers Edition)

Fresh Spring Vibes For Your Desktop (March 2019 Wallpapers Edition)

Cosima Mielke

Spring is coming! With March just around the corner, nature is slowly but surely awakening from its winter sleep. And, well, even if spring seems far away in your part of the world, this month’s wallpaper selection is bound to at least get your ideas springing.

Just like every month since more than nine years already, artists and designers from across the globe got out their favorite tools and designed unique wallpapers to cater for some fresh inspiration on your desktop and mobile screens. The wallpapers come in versions with and without a calendar for March 2019 and can be downloaded for free. A big thank-you to everyone who submitted their designs! As a little bonus goodie, we also added some favorites from past years’ March editions at the end of this post. Now which one will make it to your screen?

Please note that:

  • All images can be clicked on and lead to the preview of the wallpaper,
  • You can feature your work in our magazine by taking part in our Desktop Wallpaper Calendar series. We are regularly looking for creative designers and artists to be featured on Smashing Magazine. Are you one of them?

Further Reading on SmashingMag:

Time To Wake Up

“Rays of sunlight had cracked into the bear’s cave. He slowly opened one eye and caught a glimpse of nature in blossom. Is it spring already? Oh, but he is so sleepy. He doesn’t want to wake up, not just yet. So he continues dreaming about those sweet sluggish days while everything around him is blooming.” — Designed by PopArt Studio from Serbia.

Time To Wake Up

Queen Bee

“Spring is coming! Birds are singing, flowers are blooming, bees are flying… Enjoy this month!” — Designed by Melissa Bogemans from Belgium.

Queen Bee

Bunny O’Hare

“When I think of March I immediately think of St. Patrick’s Day and my Irish heritage… and then my head fills with pub music! I had fun putting a twist on this month’s calendar starring my pet rabbit. Erin go Braugh.” — Designed by Heather Ozee from the United States.

Bunny O’Hare

A Bite Of Spring

Designed by Ricardo Gimenes from Sweden.

A Bite Of Spring


“International Women’s Day on March 8th is the inspiration behind this artwork. Through this artwork, we wish a jovial, strong and successful year ahead for all women around the world.” — Designed by Sweans Technologies from London.


Stunning Beauty

“A recent vacation to the Philippines led me to Palawan, specifically El Nido, where I was in awe of the sunset. I wanted to emphasize the year in the typography as a reminder that, even though we are three months in, our resolutions are still fresh and new and waiting for us to exceed them! Photograph shot by @chrishernando, whose companionship and permission I am so grateful for.” — Designed by Mary Walker from the United States.

Stunning Beauty

Spring Time!

“Spring is here! Giraffes are starting to eat the green leaves.” — Designed by Veronica Valenzuela from Spain.

Spring Time!


“The legend of St. Patrick banishing snakes from Ireland.” — Designed by Caitey Kennedy from the United States.


Oldies But Goodies

In more than nine years running this community project, a lot of wallpaper gems have accumulated in our archives. Let’s take a look back and rediscover some March favorites from past years. Please note that these wallpapers don’t come with a calendar.

Let’s Get Outside

“Let’s get outside and seize the beginning of Spring. Who knows what adventures might await us there?” — Designed by Lívia Lénárt from Hungary.

Let’s Get Outside!

The Unknown

“I made a connection, between the dark side and the unknown lighted and catchy area.” — Designed by Valentin Keleti from Romania.

The Unknown


Designed by Romana Águia Soares from Portugal.


Spring Bird

Designed by Nathalie Ouederni from France.

Spring Bird


“A day, even a whole month aren’t enough to show how much a woman should be appreciated. Dear ladies, any day or month are yours if you decide so.” — Designed by Ana Masnikosa from Belgrade, Serbia.


Wake Up!

“Early spring in March is for me the time when the snow melts, everything isn’t very colorful. This is what I wanted to show. Everything comes to life slowly, as this bear. Flowers are banal, so instead of a purple crocus we have a purple bird-harbinger.” — Designed by Marek Kedzierski from Poland.

Wake Up!

Spring Is Coming!

“Spring is the best part of the year! Nature breaking free and spring awakening is symbolic of our awakening.” — Designed by Silvia Bukovac from Croatia.

Spring Is Coming!

Spring Is Inevitable!

“Spring is round the corner. And very soon plants will grow on some other planets too. Let’s be happy about a new cycle of life.” — Designed by Igor Izhik from Canada.

Spring Is Inevitable!

Tune In To Spring!

Designed by Iquadart from Belarus.

Tune in to spring!

Wake Up!

“I am the kind of person that prefers cold but I do love spring since it’s the magical time when flowers and trees come back to life and fill the landscape with beautiful colors.” — Designed by Maria Keller from Mexico.

Wake up!

Let’s Spring!

“After some freezing months, it’s time to enjoy the sun and flowers. It’s party time, colours are coming, so let’s spring!” — Designed by Colorsfera from Spain.

Let's spring!

MARCHing Forward!

“If all you want is a little orange dinosaur MARCHing (okay, I think you get the pun) across your monitor, this wallpaper was made just for you! This little guy is my design buddy at the office and sits by (and sometimes on top of) my monitor. This is what happens when you have designer’s block and a DSLR.” — Designed by Paul Bupe Jr from Statesboro, GA.

MARCHing forward!

Waiting For Spring

“As days are getting longer again and the first few flowers start to bloom, we are all waiting for Spring to finally arrive.” Designed by Naioo from Germany.

Smashing Wallpaper - march 12

March Fusion

Designed by Rio Creativo from Poland.

Smashing Wallpaper - march 12


“A daydream is a visionary fantasy, especially one of happy, pleasant thoughts, hopes or ambitions, imagined as coming to pass, and experienced while awake.” Designed by Bruna Suligoj from Croatia.

Smashing Wallpaper - march 11

Sweet March

“Digital collage, based on past and coming spring. The idea is to make it eternal or at least make it eternal in our computers! Hope you like it.” Designed by Soledad Martelletti from Argentina.

Smashing Wallpaper - march 11


“Exploring new worlds is much like exploring your own mind, creativity and knowledge. The only way to learn what’s really inside you is by trying something new. The illustration is my very own vision of the knowledge. It’s placed in some mysterious habitat. It’s a space where people learn from each other, find new talents and study their own limits.” — Designed by Julia Wójcik from Poland.


Join In Next Month!

Please note that we respect and carefully consider the ideas and motivation behind each and every artist’s work. This is why we give all artists the full freedom to explore their creativity and express emotions and experience throughout their works. This is also why the themes of the wallpapers weren’t anyhow influenced by us but rather designed from scratch by the artists themselves.

Thank you to all designers for their participation. Join in next month!


For the 30th anniversary of the web, CERN brought nine web nerds together to recreate the very first web browser — Or a working replication of it anyway, as you use it from your web browser, inception style.

Well done, Mark Boulton, John Allsopp, Kimberly Blessing, Jeremy Keith, Remy Sharp, Craig Mod, Martin Akolo Chiteri, Angela Ricci, and Brian Suda! I love that it was written in React and the font is an actual replication of what was used back then. What a cool project.

They even opened-sourced the code.

You can visit any site with Document > Open from full document reference:

CSS-Tricks ain’t too awful terrible, considering the strictness:

When this source code was written, there was no version number associated with HTML. Based on the source code, the following tags—the term ‘element’ was not yet used—were recognized:

  • A
  • DL, DT, DD
  • H1, H2, H3, H4, H5, H6
  • HP1, HP2, HP3
  • LI
  • NODE
  • OL
  • PRE
  • UL
  • XMP

Unrecognized tags were considered junk and ignored.

Direct Link to ArticlePermalink

The post WorldWideWeb appeared first on CSS-Tricks.

Breaking Boxes With CSS Fragmentation

Breaking Boxes With CSS Fragmentation

Breaking Boxes With CSS Fragmentation

Rachel Andrew

In this article, I’m going to introduce you to the CSS Fragmentation specification. You might never have heard of it, however, if you have ever created a print stylesheet and wanted to control where the content breaks between pages, or multi-column layout and wanted to stop a figure breaking between columns, you have encountered it.

I find that quite often problems people report with multicol are really problems with browser support of fragmentation. After a quick rundown of the properties contained in this specification, I’ll be explaining the current state of browser support and some of the things you can do to get it working as well as it can in your multicol and print projects.

What Is Fragmentation?

Fragmentation in CSS describes the process by which content becomes broken up into different boxes. Currently, we have two places in which we might run into fragmentation on the web: when we print a document, and if we use multi-column layout. These two things are essentially the same. When you print (or save to PDF) a webpage, the content is fragmented into as many pages as are required to print your content.

When you use multicol, the content is fragmented into columns. Each column box is like a page in the paged context. If you think of a set of columns as being much like a set of pages it can be a helpful way to think about multicol and how fragmentation works in it.

If you take a look at the CSS Fragmentation Specification you will see a third fragmented context mentioned — that context is Regions. As there are no current usable implementations of Regions, we won’t be dealing with that in this article, but instead looking at the two contexts that you might come across in your work.

Block And Inline Boxes

I am going to mention block boxes a lot in this article. Every element of your page has a box. Some of those boxes are laid out as blocks: paragraphs, list items, headings. These are said to be participating in a block formatting context. Others are inline such as the words in a paragraph, spans and anchor elements. These participate in an inline formatting context. Put simply, when I refer to a block box, I’m talking about boxes around things like paragraphs. When dealing with fragmentation, it is important to know which kind of box you are dealing with.

For more information on block and inline layout, see the MDN article “Block And Inline Layout In Normal Flow”. It is one of those things that we probably all understand on some level but might not have encountered the terminology of before.

Controlling Breaks

Whether you are creating a print stylesheet, using a specific print user agent to make a PDF,or using multicol, you will sometimes run into problems that look like this.

In the below multicol example, I have some content which I am displaying as three columns. In the middle of the content is a boxed out area, which is being broken across two columns. I don’t want this behavior — I would like the box to stay together.

Three columns with a boxed out area broken across two of them
The box breaks across two columns (Large preview)

To fix this, I add the property break-inside: avoid to the box. The break-inside property controls breaks inside elements when they are in a fragmented context. In a browser which supports this property, the box will now stay in one of the columns. The columns will look less well balanced, however, that is generally a better thing than ending up with the boxout split across columns.

The break-inside property is one of the properties detailed in the fragmentation spec. The full list of properties is as follows:

  • break-before
  • break-after
  • break-inside
  • orphans
  • widows
  • box-decoration-break

Let’s have a look at how these are supposed to work before we move onto what actually happens in browsers.

The break-before And break-after Properties

There are two properties that control breaks between block-level boxes: break-before and break-after. If you have an h2 followed by two paragraphs <p> you have three block boxes and you would use these properties to control the breaks between the heading and first paragraph, or between the two paragraphs.

The properties are used on selectors which target the element you want to break before or after.

For example, you might want your print stylesheet to break onto a new page every time there is a level 2 heading. In this case, you would use break-before: page on the h2 element. This controls the fragmentation and ensures there is always a break before the box of the h2 element.

h2 { break-before: page;

Another common requirement is to prevent headings ending up as the last thing on a page or column. In this case, you might use break-after with a value of avoid. This should prevent a break directly after the box of the element:

h1, h2, h3, h4 { break-after: avoid;
Fragments Within Fragments

It is possible that you might have an element that is fragmented nested inside another. For example, having a multicol inside something which is paged. In that case, you might want to control breaks for pages but not for columns, or the other way around. This is why we have values such as page which would always force a break before or after the element but only when the fragment is a page. Or avoid-page which would avoid a break before or after the element only for paged contexts.

The same applies to columns. If you use the value column, this would always force a break before or after that element, but only for multicol contexts. The value avoid-column would prevent a break in multicol contexts.

There is an always value in the Level 4 specification which indicates that you want to break through everything – page or column. However, as a recent addition to the spec it is not currently useful to us.

Additional Values For Paged Media

If you are creating a book or magazine, you have left and right pages. You might want to control breaking in order to force something onto the left or right page of a spread. Therefore, using the following would insert one or two-page breaks before the h2 to ensure it was formatted as a right page.

h2 { break-before: right;

There are also recto and verso values which relate to page progression as books written in a vertical or right to left language have a different page progression than books written in English. I’m not going to cover these values further in this article as I’m primarily concerned with what is possible from the browser this time.


We have already seen an example of the break-inside property. This property controls breaking inside block boxes, e.g. inside a paragraph, heading or a div.

Things that you may not want to break can include a boxout as described above: figures where you do not want the caption detached from the image, tables, lists and so on. Add break-inside: avoid to any container you don’t wish to break in any fragmentation context. If you only wish to avoid breaks between columns use break-inside: avoid-column and between pages break-inside: avoid-page.

The orphans And widows Properties

The orphans and widows properties deal with how many lines should be left before or after a break (either caused by a column or a new page). For example, if I want to avoid a single line being left at the end of a column, I would use the orphans property, as in typography, an orphan is the first line of a paragraph that appears alone at the bottom of a page with the rest of the paragraph broken onto another page. The property should be added to the same element which is fragmenting (in our case, the multicol container).

.container { column-count: 3; orphans: 2;

To control how many lines should be at the top of a column or page after a break, use widows:

.container { column-count: 3; widows: 2;

These properties deal with breaks between inline boxes such as the lines of words inside a paragraph. Therefore, they don’t help in the situation where a heading or other block element is alone at the bottom of a column or page, you need the break properties discussed above for that.

Box Decoration

A final property that may be of interest is the box-decoration-break property. This controls the situation where you have a box with a border broken between two column boxes or pages. Do you want the border to essentially be sliced in half? Or do you want each of the two halves of the box to be wrapped fully in a border?

The first scenario is the default, and is as if you set the box-decoration-break property to slice on the box.

.box { box-decoration-break: slice;
A box with a border which is broken between columns
A value of slice means the border is effectively sliced in half (Large preview)

To get the second behavior, set box-decoration-break to clone.

.box { box-decoration-break: clone;
Boxes are completely wrapped in borders
A value of clone means the border is wrapped fully round each fragment of the box (Large preview)

Browser Support For Fragmentation

Now we come to the reason I don’t have a bunch of CodePen examples above to demo all of this to you, and the main reason for my writing this article. Browser support for these properties is not great.

If you are working in Paged Media with a specific user agent such as Prince, then you can enjoy really good support for fragmentation, and will probably find these properties very useful. If you are working with a web browser, either in multicol, creating print stylesheets, or using something like Headless Chrome to generate PDFs, support is somewhat patchy. You’ll find that the browser with the best support is Edge — until it moves to Chromium anyway!

Can I Use isn’t overly helpful with explaining support due to mixing the fragmentation properties in with multicol, then having some separate data for legacy properties. So, as part of the work I’ve been doing for MDN to document the properties and their support, I began testing the actual browser support. What follows is some advice based on that testing.

Legacy And Vendor Prefixed Properties

I can’t go much further without a history lesson. If you find you really need support for fragmentation then you may find some relief in the legacy properties which were originally part of CSS2 (or in some prefixed properties that exist).

In CSS2, there were properties to control page breaking. Multicol didn’t exist at that point, so the only fragmented context was a paged one. This meant that three specific page breaking properties were introduced:

  • page-break-before
  • page-break-after
  • page-break-inside

These work in a similar way to the more generic properties without the page- prefix, controlling breaks before, after and inside boxes. For print stylesheets, you will find that some older browsers which do not support the new break- properties, do support these page prefixed properties. The properties are being treated as aliases for the new properties.

In a 2005 Working Draft of the multicol specification are details of breaking properties for multicol — using properties prefixed with column- (i.e. column-break-before, column-break-after, and column-break-inside). By 2009, these had gone, and a draft was in the multicol specification for unprefixed break properties which eventually made their way into the CSS Fragmentation specification.

However, some vendor prefixed column-specific properties were implemented based on these properties. These are:

  • -webkit-column-break-before
  • -webkit-column-break-after
  • -webkit-column-break-inside

Support For Fragmentation In Multicol

The following is based on testing these features in multicol contexts. I’ve tried to explain what is possible, but do take a look at the CodePens in whichever browsers you have available.

Multicol And break-inside

Support in multicol is best for the break-inside property. Up to date versions of Chrome, Firefox, Edge, and Safari all support break-inside: avoid. So you should find that you can prevent boxes from breaking between columns when using multicol.

Several browsers, with the exception of Firefox, support the -webkit-column-break-inside property, this can be used with a value of avoid and may prevent boxes breaking between columns which do not have support for break-inside.

Firefox supports page-break-inside: avoid in multicol. Therefore, using this property will prevent breaks inside boxes in Firefox browsers prior to Firefox 65.

This means that if you want to prevent breaks between boxes in multicol, using the following CSS will cover as many browsers as possible, going back as far as possible.

.box { -webkit-column-break-inside: avoid; page-break-inside: avoid; break-inside: avoid;

As for the column value, explicitly stating that you only want to avoid breaks between columns, and not pages, works in all browsers except Firefox.

The below CodePen rounds up some of these tests in multicol so you can try them for yourself.

Multicol And break-before

In order to prevent breaks before an element, you should be able to use break-before: avoid or break-before: avoid-column. The avoid property has no browser support.

Edge supports break-before: column to always force a break before the box of the element.

Safari, Chrome and Edge also support -webkit-column-break-before: always which will force a break before the box of the element. Therefore, if you want to force a break before the box of an element, you should use:

.box { -webkit-column-break-before: always; break-before: column;

Preventing a break before the box is currently an impossible task. You can play around with some examples of these properties below:

Multicol And break-after

To prevent breaks after an element, to avoid it becoming the last thing at the bottom of a column, you should be able to use break-after: avoid and break-after: avoid-column. The only browser with support for these is Edge.

Edge also supports forcing breaks after an element by using break-after: column, Chrome supports break-after: column and also -webkit-column-break-after: always.

Firefox does not support break-after or any of the prefixed properties to force or allow breaks after a box.

Therefore, other than Edge, you cannot really avoid breaks after a box. If you want to force them, you will get results in some browsers by using the following CSS:

.box { -webkit-break-after: always; break-after: column;

Support When Printing From The Browser

Whether you print directly from your desktop browser or generate PDF files using headless Chrome or some other solution reliant on browser technology doesn’t make any difference. You are reliant on the browser support for the fragmentation properties.

If you create a print stylesheet you will find similar support for the break properties as for multicol, however, to support older browsers you should double up the properties to use the page- prefixed properties.

In modern browsers ,the break-inside property can be used to prevent breaks inside boxes, add the page-break-inside property to add support for older browsers.

.box { page-break-inside: avoid; break-inside: avoid;

To force breaks before a box use break-before:page along with page-break-before: always.

.box { page-break-before: always; break-before: page;

To avoid breaks before a box use break-before: avoid-page along with page-break-before: avoid.

.box { page-break-before: avoid; break-before: avoid-page;

There is better support for the page and avoid-page values than we see for the equivalent multicol values. The majority of modern browsers have support.

To force breaks after a box, use break-after: page along with page-break-after: always.

.box { page-break-after: always; break-after: page;

To prevent breaks after a box use break-after: avoid-page along with page-break-after: avoid.

.box { page-break-after: avoid; break-after: avoid-page;

Widows And Orphans

The widows and orphans properties enjoy good cross-browser support — the only browser without an implementation being Firefox. I would suggest using these when creating a multicol layout or print stylesheet. If they don’t work for some reason, you will get widows and orphans, which isn’t ideal but also isn’t a disaster. If they do work your typography will look all the better for it.


The final property of box-decoration-break has support for multicol and print in Firefox. Safari, Chrome and other Chromium-based browsers support -webkit-box-decoration-break, but only on inline elements. So you can clone borders round lines of a sentence for example; they do not have support in the context we are looking at.

In the CodePen below, you can see that testing for -webkit-box-decoration-break: clone with Feature Queries returns true, however, the property has no effect on the border of the box in the multicol context.

Using Fragmentation

As you can see, the current state of fragmentation in browsers is somewhat fragmented! That said, there is a reasonable amount you can achieve and where it fails, the result tends to be suboptimal but not a disaster. Which means it is worth trying.

It is worth noting that being too heavy handed with these properties could result in something other than what you hoped for. If you are working on the web rather than print and force column breaks after every paragraph, then end up with more paragraphs than space for columns, multicol will end up overflowing in the inline direction. It will run out of columns to place your additional paragraphs. Therefore, even where there is support, you still need to test carefully, and remember that less is more in a lot of cases.

More Resources

To read more about the properties head over to MDN, I’ve recently updated the pages there and am also trying to keep the browser compat data up to date. The main page for CSS Fragmentation links to the individual property pages which have further examples, browser compat data and other information about using these properties.

Smashing Editorial (il)
Look Ma, No Media Queries! Responsive Layouts Using CSS Grid

Not only has CSS Grid reshaped the way we think and build layouts for the web, but it has also contributed to writing more resilient code, replacing “hacky” techniques we’ve used before, and in some cases, killing the need to rely on code for specific resolutions and viewports. What’s so cool about this era in web development is that we’re capable of doing more and more with fewer lines of code.

In this article, we’ll start dipping our toes into the power of CSS Grid by building a couple of common responsive navigation layouts. It’s easier than what you may think, and since CSS Grid was built with responsiveness in mind, it’ll take less code than writing media queries all over the place. Let’s do this!

Layout #1: Hero content and list of articles

See the Pen
Hero Content and List of Articles
by Juan Martín García (@imjuangarcia)
on CodePen.

We’ll kick off this set of examples by creating a common website layout: A full-width hero section, with a grid of cards below.

Both elements will respond to window resizing and adapt accordingly. Though this might seem like a lot of code at first glance, the responsive behavior is done with only six lines of CSS Grid code, and without writing a single @media rule. Let’s break down the code to see what’s going on:

The hero section

Let’s take a look at the code for the .hero element:

<section class="hero"> <h1>You thirsty?</h1> <article> <p>Explore local breweries with just one click and stirred by starlight across the centuries light years great turbulent clouds circumnavigated paroxysm of global death.</p> <a href="#breweries">Browse Breweries</a> </article>
.hero { /* Photo by mnm.all on Unsplash */ background: url('https://images.unsplash.com/photo-1518176258769-f227c798150e') center; background-size: cover; padding: 4rem 2rem; /* Grid styles */ display: grid; align-items: center; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));

We have a bunch of background styles to enable the beer background, a bit of padding to separate the content from the edge of the screen, and then three lines of grid styles:

  1. The first line (display: grid;) is changing the behavior of the .hero element to be a grid container. That means the elements inside .hero are now grid items.
  2. The second line (align-items: center;) is going to vertically center the columns on our grid. But these two lines don’t do anything on their own until we set the columns of our grid.
  3. And that’s where the third line comes in. A lot of stuff is going on in that single property, so let’s go one step at a time.

The repeat() function

Generally speaking, what we usually do to define our columns and rows on a CSS Grid is to add the value for each track after defining the property, like this:

.element { /* This will result on four columns, each one of 1fr */ grid-template-columns: 1fr 1fr 1fr 1fr; /* This will result on two rows, each one of 300px */ grid-template-rows: 300px 300px;

Now, that’s quite dull. We can use the repeat() function to make that less verbose and easier to follow. The function takes two parameters:

  1. The number of times to repeat the value.
  2. The value itself.

After refactoring our code to use repeat(), we should expect the same results from these lines of code:

.element { /* this is the same as grid-template-columns: 1fr 1fr 1fr 1fr; */ grid-template-columns: repeat(4, 1fr); /* this is the same as grid-template-rows: 300px 300px; */ grid-template-rows: repeat(2, 300px);

Much cleaner, yeah?

The minmax() function

Now, the above examples are explicitly defining sizes for the tracks (1fr and 300px). That might work for some scenarios, but for our beer example here, we need to be able to automatically calculate the size of the track, based on the width of the viewport, and automatically adjust the number of columns shown. To be able to do that, we’ll define a range of values using the minmax() function. What will we be defining? You’ve probably guessed by now: The *minimum* and *maximum* values we want these columns to be able to resize to.

In the hero for our beer example above, we set our minmax() property to be 240px at its minimum size, and 1fr at its maximum size. fr units, if you’ve never heard of them, stand for fractional units. Nobody can explain them better than Jen Simmons on this video and Robin Rendle in this post.

Using the Firefox Grid Inspector to check the change on the track’s size when resizing

That results in our tracks being 1fr when there’s plenty of space on our viewport (aka desktop resolutions), and 240px when there’s not enough space for both columns (like on mobile devices). That’s why they nicely grow when we make our browser wider, since they’re taking the remaining space and equally dividing it across the existing columns. Now, moving to the last piece of the puzzle!

The auto-fit keyword

The auto-fit keyword allows us to wrap our columns into rows when there’s not enough space in our viewport to fit the 240px minimum value without overflowing the content. Sara Soueidan wrote an excellent article about auto-sizing columns using the auto-fill and auto-fit keywords, in case you want to dive a little deeper into what’s going on under the hood. Now, with that last bit of code in place, we should be able to achieve this result:

The column is automatically wrapping when there’s not enough space in the viewport

The article list

Now that we’ve thoroughly reviewed the behavior of the elements inside our hero element, it’s likely that the first two lines of CSS code for the breweries list below it might already seem familiar to you:

.breweries > ul { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); grid-gap: 1rem;

That’s right! We’re using the exact same approach: On the first line we define our grid, on the second one we size our tracks using the same magic one-liner, and on the third line we set a gap for these columns. Nothing new under the sun, and what’s really neat about this, is that our code is resilient enough to adjust the number of tracks and their sizes, according to the number of items we have inside our unordered list:

The grid responds to the change in the number of tracks, and adapts the layout

That’s all, folks! A fully responsive website layout, using just six lines of CSS code. Not bad, huh? Make sure you check the source code and play around with this example on CodePen.

Layout #2: Full-width image gallery

See the Pen
Full Width Image Gallery
by Juan Martín García (@imjuangarcia)
on CodePen.

On this next example, we’ll embrace the power of our newly learned combination of repeat(), auto-fit and minmax() to create this responsive image gallery. We’ll also be sizing our tracks using grid-column and grid-row, and learning about the handy property:value combination of grid-auto-flow: dense; that allows us to change the default behavior of the elements that can’t fit on our explicit tracks: Instead of wrapping themselves in new rows or columns, we’ll make them fit into the unused spots on our grid. Let’s get into the coding!

The grid setup

The grid is created using our familiar display: grid; property, where columns are defined using repeat(), auto-fit and minmax(). We also added a bunch rows with a repeat() function and defined a gap to our images, using grid-gap. But the new player here is the grid-auto-flow: dense;. We’ll get to it in a second.

.gallery > .gallery__list { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); grid-template-rows: repeat(6, 200px); grid-gap: 1rem; grid-auto-flow: dense;

We also created a repetition pattern using the nth-child() pseudo-selector to set different sizes for our tracks using grid-column and grid-row. Notice here that we’re using the span keyword to allow the selected item to occupy more than one column or row.

/* This will create 2x images every 4 elements */
.gallery > .gallery__list > li:nth-child(4n) { grid-column: span 2; /* Spans two columns */ grid-row: span 2; /* Spans two rows */
} /* This will create 3x images every 8 elements */
.gallery > .gallery__list > li:nth-child(8n) { grid-column: span 3; grid-row: span 3;

And finally, we’ll make sure our images cover the entire area of its container, regardless if it’s 1x, 2x or 3x, using object-fit: cover;. If you have never heard of object-fit, it works fairly similar to how background-image does, but with HTML <img> tags:

.gallery > .gallery__list > li > figure > img { width: 100%; height: 100%; object-fit: cover;

Now, the real deal here is grid-auto-flow: dense;. Check what happens when we take that out from our code:

Removing grid-auto-flow: dense; leads to inconsistent placement of the elements on the grid

See those holes on our beautifully crafted grid? That’s because some of the elements on it are taking 2x or 3x spots, and when there isn’t enough space on our tracks to fit them, they’ll wrap into a new row, since that’s the default behavior. By changing it from row to dense, we’re telling the grid to fill any gaps we might have with elements that could fit them, regardless of their source order on the DOM.

That’s why this technique might come especially handy for things like image galleries, but might not be suitable for other use cases where you might need to preserve the order of the markup. Feel free to play around with the CodePen demo to check the differences between where items are placed.

Layout #3: Trello-style card layout

See the Pen
Trello-Style Card Layout
by Juan Martín García (@imjuangarcia)
on CodePen.

Now, on to the last demo, where we’ll take advantage of the ability to nest grids to recreate this Trello Board. We’ll be creating a grid to hold our four different columns, and inside of those, we’ll create a child grid for our cards. Even though this example won’t explore new properties or revolutionary methods, it’ll help us to get a grasp on how easy it is to build complex layouts with a few lines of CSS code. This demo has a lot of extra code to achieve the styling of the Trello layout, so we’ll focus solely on the grid styles.

The columns

To create the four columns, we’ll use display: grid; on the container and use our magical one-liner for our grid-template-columns. We’ll also be defining a gap between them, and use align-items: flex-start; to ensure that our columns don’t stretch to the bottom of the screen.

.column__list { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); grid-gap: .5rem; align-items: flex-start;

Now, the original Trello is not responsive by default: If you resize your browser on a Trello Board, you’ll notice that you’ll end up having a horizontal scroll on your columns, rather than wrapping them on a new row. We’re not following that behavior here since we want to build responsive layouts, but in case you’re curious, and want to emulate Trello’s functionality, you can achieve that by adding two more lines of CSS code:

.column__list { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); grid-gap: .5rem; align-items: flex-start; /* Uncomment these lines if you want to have the standard Trello behavior instead of the column wrapping */ grid-auto-flow: column; grid-auto-columns: minmax(260px, 1fr);

We learned about grid-auto-flow in our previous demo and discovered that it let us control how the auto-placement algorithm work, and how implicit elements should be added in the flow of the grid. The default behavior is row, meaning that any extra element that won’t fit on our grid will wrap into a new line. We changed that to be dense on our previous demo, and we’ll change it to be column on this one: That way, any new column added here will end up in an implicit column, and have a horizontal scroll. We’ll also define a width for those auto-generated columns with the grid-auto-columns property.

Modifying the grid-auto-flow property will make this demo behave like the real-world Trello

The cards

For the cards grid, we’ll use a similar approach. We’ll display: grid; on the container. We won’t define any columns here, since we don’t want to have any, and we’ll put grid-template-rows: auto; to use to avoid all cards having the same height — we want some of them to be bigger and some of them smaller, based on the type of content being added to them.

.card__list { display: grid; grid-template-rows: auto; grid-gap: .5rem; margin: .5rem 0;

And, again, that’s all folks! Two more lines to set a gap and a margin to the cards, and we’re done! Everything else in the Pen is standard CSS to achieve the Trello look and feel.

So then… are media queries dead?

Back in the day, when we were building layouts using display: inline-block or floats, media queries made a lot of sense in order to change the size of our elements as the viewport got smaller. But now, with the incredibly powerful layouts that we’re able to create with a couple of CSS lines, you might feel tempted to think that media queries are doomed. I strongly disagree with that: I believe that we should change the way we think about them, and therefore use them differently.

As Rachel Andrew stated about a year ago, we should use media queries to fix our layout when it breaks, rather than targeting devices: There are so many out there! With the advent of Media Queries Level 4 and 5, we’re not only able to detect screen sizes now, but pointer types as well. As a result, we can dig into a user’s system preferences and adapt our code for those who prefer reduced motion or whether we should use inverted colors. That means media queries are not dead; on the flipside, I’d say it’s an exciting time for using media queries, but we need to learn to use them right. In the meantime, building robust layouts using modern techniques such as Flexbox or CSS Grid, will save you a bunch of time, code, and headaches.

The post Look Ma, No Media Queries! Responsive Layouts Using CSS Grid appeared first on CSS-Tricks.

Sliding In And Out Of Vue.js

Sliding In And Out Of Vue.js

Sliding In And Out Of Vue.js

Kevin Ball

Vue.js has achieved phenomenal adoption growth over the last few years. It has gone from a barely known open-source library to the second most popular front-end framework (behind only React.js).

One of the biggest reasons for its growth is that Vue is a progressive framework — it allows you to adopt bits and pieces at a time. Don’t need a full single page application? Just embed a component. Don’t want to use a build system? Just drop in a script tag, and you’re up and running.

This progressive nature has made it very easy to begin adopting Vue.js piecemeal, without having to do a big architecture rewrite. However, one thing that is often overlooked is that it’s not just easy to embed Vue.js into sites written with other frameworks, it’s also easy to embed other code inside of Vue.js. While Vue likes to control the DOM, it has lots of escape hatches available to allow for non-Vue JavaScript that also touches the DOM.

This article will explore the different types of third-party JavaScript that you might want to use, what situations you might want to use them inside of a Vue project, and then cover the tools and techniques that work best for embedding each type within Vue. We’ll close with some considerations of the drawbacks of these approaches, and what to consider when deciding if to use them.

This article assumes some familiarity with Vue.js, and the concepts of components and directives. If you are looking for an introduction to Vue and these concepts, you might check out Sarah Drasner’s excellent introduction to Vue.js series or the official Vue Guide.

Types Of Third-Party JavaScript

There are three major types of third-party JavaScript that we’ll look at in order of complexity:

  1. Non-DOM Touching Libraries
  2. Element Augmentation Libraries
  3. Components And Component Libraries

Non-DOM Libraries

The first category of third-party JavaScript is libraries that provide logic in the abstract and have no direct access to the DOM. Tools like moment.js for handling dates or lodash for adding functional programming utilities fall into this category.

These libraries are trivial to integrate into Vue applications, but can be wrapped up in a couple of ways for particularly ergonomic access. These are very commonly used to provide utility functionality, the same as they would in any other type of JavaScript project.

Element Augmentation Libraries

Element augmentation is a time-honored way to add just a bit of functionality to an element. Examples include tasks like lazy-loading images with lozad or adding input masking using Vanilla Masker.

These libraries typically impact a single element at a time, and expect a constrained amount of access to the DOM. They will likely be manipulating that single element, but not adding new elements to the DOM.

These tools typically are tightly scoped in purpose, and relatively straightforward to swap out with other solutions. They’ll often get pulled into a Vue project to avoid re-inventing the wheel.

Components And Component Libraries

These are the big, intensive frameworks and tools like Datatables.net or ZURB Foundation. They create a full-on interactive component, typically with multiple interacting elements.

They are either directly injecting these elements into the DOM or expect a high level of control over the DOM. They were often built with another framework or toolset (both of these examples build their JavaScript on top of jQuery).

These tools provide extensive functionality and can be challenging to replace with a different tool without extensive modifications, so a solution for embedding them within Vue can be key to migrating a large application.

How To Use In Vue

Non-DOM Libraries

Integrating a library that doesn’t touch the DOM into a Vue.js project is relatively trivial. If you’re using JavaScript modules, simply importor require the module as you would in another project. For example:

import moment from 'moment'; Vue.component('my-component', { //… methods: { formatWithMoment(time, formatString) { return moment(time).format(formatString); },

If using global JavaScript, include the script for the library before your Vue project:

One additional common way to layer on a bit more integration is to wrap up your library or functions from the library using a filter or method to make it easy to access from inside your templates.

Vue Filters

Vue Filters are a pattern that allows you to apply text formatting directly inline in a template. Drawing an example from the documentation, you could create a ‘capitalize’ filter and then apply it in your template as follows:

{{myString | capitalize}}

When importing libraries having to do with formatting, you may want to wrap them up as a filter for ease of use. For example, if we are using moment to format all or many of our dates to relative time, we might create a relativeTime filter.

const relativeTime = function(value) { if (!value) return ''; return moment(value).fromNow();

We can then add it globally to all Vue instances and components with the Vue.filter method:

Vue.filter(’relativeTime', relativeTime);

Or add it to a particular component using the filters option:

const myComponent = { filters: { ’relativeTime': relativeTime, } }

You can play with this on CodePen here:

See the Pen Vue integrations: Moment Relative Value Filter by Kevin Ball.

Element Augmentation Libraries

Element augmentation libraries are slightly more complex to integrate than libraries that don’t touch the DOM — if you’re not careful, Vue and the library can end up at cross purposes, fighting each other for control.

To avoid this, you need to hook the library into Vue’s lifecycle, so it runs after Vue is done manipulating the DOM element, and properly handles updates that Vue instigates.

This could be done in a component, but since these libraries typically touch only a single element at a time, a more flexible approach is to wrap them in a custom directive.

Vue Directives

Vue directives are modifiers that can be used to add behavior to elements in your page. Vue ships with a number of built-in directives that you are likely already comfortable with — things like v-on, v-model, and v-bind. It is also possible to create custom directives that add any sort of behavior to an element — exactly what we’re trying to achieve.

Defining a custom directive is much like defining a component; you create an object with a set of methods corresponding to particular lifecycle hooks, and then add it to Vue either globally by running:

Vue.directive('custom-directive', customDirective);

Or locally in a component by adding it to the directives object in the component:

const myComponent = { directives: { 'custom-directive': customDirective, } }
Vue Directive Hooks

Vue directives have the following hooks available to define behavior. While you can use all of them in a single directive, it is also not uncommon to only need one or two. They are all optional, so use only what you need.

  • bind(el, binding, vnode)
    Called once and only once, when the directive is first bound to an element. This is a good place for one-time setup work, but be cautious, i.e. the element exists, may not yet actually be in the document.
  • inserted(el, binding, vnode)
    Called when the bound element has been inserted into its parent node. This also does not guarantee presence in the document, but does mean if you need to reference the parent you can.
  • update(el, binding, vnode, oldVnode)
    Called whenever the containing component’s VNode has updated. There are no guarantees that other children of the component will have updated, and the value for the directive may or may not have changed. (You can compare binding.value to binding.oldValue to see and optimize away any unnecessary updates.)
  • componentUpdated(el, binding, vnode, oldVnode)
    Similar to update, but called after all children of the containing component have updated. If the behavior of your directive depends on its peers (e.g. v-else), you would use this hook instead of update.
  • unbind(el, binding, vnode)
    Similar to bind, this is called once and only once, when the directive is unbound from an element. This is a good location for any teardown code.

The arguments to these functions are:

  • el: The element the directive is bound to;
  • binding: An object containing information about the arguments and value of the directive;
  • vnode: The virtual node for this element produced by Vue’s compiler;
  • oldVNode: The previous virtual node, only passed to update and componentUpdated.

More information on these can be found in the Vue Guide on custom directives.

Wrapping The Lozad Library In A Custom Directive

Let’s look at an example of doing this type of wrapping using lozad, a lazy-loading library built using the Intersection Observer API. The API for using lozad is simple: use data-src instead of src on images, and then pass a selector or an element to lozad() and call observe on the object that is returned:

const el = document.querySelector('img');
const observer = lozad(el); observer.observe();

We can do this simply inside of a directive using the bind hook.

const lozadDirective = { bind(el, binding) { el.setAttribute('data-src', binding.value) ; let observer = lozad(el); observer.observe(); }
Vue.directive('lozad', lozadDirective)

With this in place, we can change images to lazy load by simply passing the source as a string into the v-lozad directive:

<img v-lozad="'https://placekitten.com/100/100'" />

You can observe this at work in this CodePen:

See the Pen Vue integrations: Lozad Directive Just Bind by Kevin Ball).

We’re not quite done yet though! While this works for an initial load, what happens if the value of the source is dynamic, and Vue changes it? This can be triggered in the pen by clicking the “Swap Sources” button. If we only implement bind, the values for data-src and src are not changed when we want them to be!

To implement this, we need to add an updated hook:

const lozadDirective = { bind(el, binding) { el.setAttribute('data-src', binding.value) ; let observer = lozad(el); observer.observe(); }, update(el, binding) { if (binding.oldValue !== binding.value) { el.setAttribute('data-src', binding.value); if (el.getAttribute('data-loaded') === 'true') { el.setAttribute('src', binding.value); } } }

With this in place, we’re set! Our directive now updates everything lozad touches whenever Vue updates. The final version can be found in this pen:

See the Pen Vue integrations: Lozad Directive With Updates by Kevin Ball.

Components And Component Libraries

The most complex third-party JavaScript to integrate is that which controls entire regions of the DOM, full-on components and component libraries. These tools expect to be able to create and destroy elements, manipulate them, and more.

For these, the best way to pull them into Vue is to wrap them in a dedicated component, and make extensive use of Vue’s lifecycle hooks to manage initialization, passing data in, and handling events and callbacks.

Our goal is to completely abstract away the details of the third-party library, so that the rest of our Vue code can interact with our wrapping component like a native Vue component.

Component Lifecycle Hooks

To wrap around a more complex component, we’ll need to be familiar with the full complement of lifecycle hooks available to us in a component. Those hooks are:

  • beforeCreate()
    Called before the component is instantiated. Pretty rarely used, but useful if we’re integrating profiling or something similar.
  • created()
    Called after the component is instantiated, but before it is added to the DOM. Useful if we have any one-off setup that doesn’t require the DOM.
  • beforeMount()
    Called just before the component is mounted in the DOM. (Also pretty rarely used.)
  • mounted()
    Called once the component is placed into the DOM. For components and component libraries that assume DOM presence, this is one of our most commonly used hooks.
  • beforeUpdate()
    Called when Vue is about to update the rendered template. Pretty rarely used, but again useful if integrating profiling.
  • updated()
    Called when Vue has finished updating the template. Useful for any re-instantiation that is needed.
  • beforeDestroy()
    Called before Vue tears down a component. A perfect location to call any destruction or deallocation methods on our third-party component
  • destroyed()
    Called after Vue has torn down a component.
Wrapping A Component, One Hook At A Time

Let’s take a look at the popular jquery-multiselect library. There exist many fine multiselect components already written in Vue, but this example gives us a nice combination: complicated enough to be interesting, simple enough to be easy to understand.

The first place to start when implementing a third-party component wrapper is with the mounted hook. Since the third-party component likely expects the DOM to exist before it takes charge of it, this is where you will hook in to initialize it.

For example, to start wrapping jquery-multiselect, we could write:

mounted() { $(this.$el).multiselect();

You can see this functioning in this CodePen:

This is looking pretty good for a start. If there were any teardown we needed to do, we could also add a beforeDestroy hook, but this library does not have any teardown methods that we need to invoke.

Translating Callbacks To Events

The next thing we want to do with this library is add the ability to notify our Vue application when the user selects items. The jquery-multiselect library enables this via callbacks called afterSelect and afterDeselect, but to make this more vue-like, we’ll have those callbacks emit events. We could wrap those callbacks naively as follows:

mounted() { $(this.$el).multiSelect({ afterSelect: (values) => this.$emit('select', values), afterDeselect: (values) => this.$emit('deselect', values) });

However, if we insert a logger in the event listeners, we’ll see that this does not provide us a very vue-like interface. After each select or deselect, we receive a list of the values that have changed, but to be more vue-like, we should probably emit a change event with the current list.

We also don’t have a very vue-like way to set values. Instead of this naive approach then, we should look at using these tools to implement something like the v-model approach that Vue provides for native select elements.

Implementing v-model

To implement v-model on a component, we need to enable two things: accepting a value prop that will accept an array and set the appropriate options as selected, and then emit an input event on change that passes the new complete array.

There are four pieces to handle here: initial setup for a particular value, propagate any changes made up to the parent, and handle any changes to value starting outside the component, and finally handle any changes to the content in the slot (the options list).

Let’s approach them one at a time.

  1. Setup With A Value Prop
    First, we need to teach our component to accept a value prop, and then when we instantiate the multiselect we will tell it which values to select.
    export default { props: { value: Array, default: [], }, mounted() { $(this.$el).multiSelect(); $(this.$el).multiSelect('select', this.value); },
  2. Handle Internal Changes
    To handle changes occurring due to the user interacting with the multiselect, we can go back to the callbacks we explored before — but ‘less naively’ this time. Instead of simply emitting what they send us, we want to turn a new array that takes into account our original value and the change made.
    mounted() { $(this.$el).multiSelect({ afterSelect: (values) => this.$emit('input', [...new Set(this.value.concat(values))]), afterDeselect: (values) => this.$emit('input', this.value.filter(x => !values.includes(x))), }); $(this.$el).multiSelect('select', this.value);

    Those callback functions might look a little dense, so let’s break them down a little.

    The afterSelect handler concatenates the newly selected value with our existing values, but then just to make sure there are no duplicates, it converts it to a Set (guarantees uniqueness) and then a destructuring to turn it back to an array.

    The afterDeselect handler simply filters out any deselected values from the current value list in order to emit a new list.

  3. Handling External Updates To Value
    The next thing we need to do is to update the selected values in the UI whenever the value prop changes. This involves translating from a declarative change to the props into an imperative change utilizing the functions available on multiselect. The simplest way to do this is to utilize a watcher on our value prop:
    watch: // don’t actually use this version. See why below value() { $(this.$el).multiselect('select', this.value); }

    However, there’s a catch! Because triggering that select will actually result in our onSelect handler, and thus use updating values. If we do this naive watcher, we will end up in an infinite loop.

    Luckily,for us, Vue gives us the ability to see the old as well as the new values. We can compare them, and only trigger the select if the value has changed. Array comparisons can get tricky in JavaScript, but for this example, we’ll take advantage of the fact that our arrays are simple (not containing objects) and use JSON stringify to do the comparison. After taking into account that we need to also deselect any that options that have been removed, our final watcher looks like this:

    watch: { value(newValue, oldValue) { if (JSON.stringify(newValue) !== JSON.stringify(oldValue)) { $(this.$el).multiSelect('deselect_all'); $(this.$el).multiSelect('select', this.value); } } },
  4. Handling External Updates To Slot
    We have one last thing that we need to handle: our multiselect is currently utilizing option elements passed in via a slot. If that set of options changes, we need to tell the multiselect to refresh itself, otherwise the new options don’t show up. Luckily, we have both an easy API for this in multiselect (the ’refresh’ function and an obvious Vue hook to hook into) updated. Handling this last case is as simple as:
    updated() { $(this.$el).multiSelect(’refresh');

    You can see a working version of this component wrapper in this CodePen:

    See the Pen Vue integrations: Multiselect Wrapper with v-model by Kevin Ball.

Drawbacks And Other Considerations

Now that we’ve looked at how straightforward it is to utilize third-party JavaScript within Vue, it’s worth discussing drawback of these approaches, and when it appropriate to use them.

Performance Implications

One of the primary drawbacks of utilizing third-party JavaScript that is not written for Vue within Vue is performance — particularly when pulling in components and component libraries or things built using entire additional frameworks. Using this approach can result in a lot of additional JavaScript that needs to be downloaded and parsed by the browser before the user can interact with our application.

For example, by using the multiselect component, we developed above means pulling in not only that component’s code, but all of jQuery as well. That can double the amount of framework related JavaScript our users will have download, just for this one component! Clearly finding a component built natively with Vue.js would be better.

Additionally, when there are large mismatches between the APIs used by third-party libraries and the declarative approach that Vue takes, you may find yourself implementing patterns that result in a lot of extra execution time. Also using the multiselect example, we had to refresh the component (requiring looking at a whole bunch of the DOM) every time a slot changed, while a Vue-native component could utilize Vue’s virtual DOM to be much more efficient in its updates.

When To Use

Utilizing third-party libraries can save you a ton of development time, and often means you’re able to use well-maintained and tested software that you don’t have the expertise to build. The primary drawback is performance, particularly when bringing in large frameworks like jQuery.

For libraries that don’t have those large dependencies, and particularly those that don’t heavily manipulate the DOM, there’s no real reason to favor Vue-specific libraries over more generic ones. Because Vue makes it so easy to pull in other JavaScript, you should go based on your feature and performance needs, simply picking the best tool for the job, without worrying about something Vue-specific.

For more extensive component frameworks, there are three primary cases in which you’d want to pull them in.

  1. Prototyping
    In this case, speed of iteration matters far more than user performance; use whatever gets the job done fastest.
  2. Migrating an existing site.
    If you’re migrating from an existing site to Vue, being able to wrap whatever framework you’re already using within Vue will give you a graceful migration path so you can gradually pull out the old code piece by piece, without having to do a big bang rewrite.
  3. When the functionality simply isn’t available yet in a Vue component.
    If you have a specific and challenging requirement you need to meet, for which a third-party library exists but there isn’t a Vue specific component, by all means consider wrapping the library that does exist.

When there are large mismatches between the APIs used by third-party libraries and the declarative approach that Vue takes, you may find yourself implementing patterns that result in a lot of extra execution time.

Examples In The Wild

The first two of these patterns are used all over the open-source ecosystem, so there are a number of different examples you can investigate. Since wrapping an entire complex component or component library tends to be more of a stopgap/migration solution, I haven’t found as many examples of that in the wild, but there are a couple out there, and I’ve used this approach for clients occasionally as requirements have dictated. Here is a quick example of each:

  1. Vue-moment wraps the moment.js library and creates a set of handy Vue filters;
  2. Awesome-mask wraps the vanilla-masker library and creates a directive for masked inputs;
  3. Vue2-foundation wraps up the ZURB Foundation component library inside of Vue components.


The popularity of Vue.js shows no signs of slowing down, with a huge amount of credit being due to the framework’s progressive approach. By enabling incremental adoption, Vue’s progressive nature means that individuals can start using it here and there, a bit at a time, without having to do massive rewrites.

As we’ve looked at here, that progressive nature extends in the other direction as well. Just as you can embed Vue bit by bit in another application, you can embed other libraries bit by bit inside of Vue.

Need some piece of functionality that hasn’t been ported to a Vue component yet? Pull it in, wrap it up, and you’re good to go.

Further Reading on SmashingMag:

Smashing Editorial (rb, ra, il)
Dogma Kills Design

It’s all in the title, really.

I am no stranger to dogmatic thinking. I was once a very religious missionary, and currently am an open source enthusiast, a web designer, and a gamer. Of all the dogmatic thinkers I’ve encountered in each of those fields, I’m not actually sure which scare me the most. Some are easily identified from a distance, but you never know who compiles their own UNIX-based OS from scratch until it’s too late.

We want to make amazing things, and/or a lot of money, and we often end up fixating rather rigidly on whatever we believe will achieve those goals

No, it’s not a one-to-one comparison, but bear with me here. I’m not saying anyone is likely to be purged in the name of free software, or because they’re on the wrong side of the Warframe vs. Anthem debate. But… it’s not completely outside the realm of possibility. Human beings in general can get a bit intense on occasion.

That’s what (I believe) dogma is, in our modern context: intense and very rigid thinking. Creative people are people of passion and drive. We want to make amazing things, and/or a lot of money, and we often end up fixating rather rigidly on whatever we believe will achieve those goals. Creative though we may be, we are not immune to the universal laws of irony.

The wonderful world of web design is thus a world of goals. We want people to engage, to interact, to stay, to buy, to tell their friends, and to be our friends if it comes to that. And then we want it to be accessible, and usable, but also based on up-to-date frameworks and it just gets confusing sometimes. To stave off confusion, we fall back on what we are pretty sure actually works. We stand our ground on a foundation of facts, and ideas that look a lot like facts, but are actually opinions.

We stand our ground on a foundation of facts, and ideas that look a lot like facts, but are actually opinions

That would be great if this weren’t an industry where the facts change on a daily basis, and everyone seems to have different facts in any case. Hold on too long to any fact or fact-like idea, and you’ll end up just being wrong.

That’s the irony of dogma. We cling to it to protect ourselves from uncertainty and the unknown. Often people cling to dogma in an attempt to shelter their communities from change. The result, of course, is stagnation. Communities, ideas, and industries that don’t evolve will always eventually die. It can take a while, but it’s inevitable.

Dogma kills thought, and halts the processes of mental evolution. Design is pretty much the visual representation of the designer’s thought process, and requires evolution to stay relevant. And so we come back to the title.

Ground Yourself With Principles, not “Facts”

Take browsers (and software in general) as the most blindingly obvious example of this principle: Internet Explorer 6 used to be nearly synonymous with the Internet as a whole. It was what nearly everyone used, and so it was the one browser you had to support. Now it’s Chrome.

As more and more browsers hit the market (there was a small explosion of them in the early-to-mid 00s), some designers and devs started asking questions like, “Well how many of these things are we actually supposed to support?”

They did not like that the answer was, “All of them, sort of.” Cooler heads then pointed out that there were ways to make sure every site you built functioned at some level in every browser. Nowadays we have names for these principles, names like: progressive enhancement, graceful degradation, and “generally not making your whole site depend on something only one browser supports right now”.

The first approach is based on a perceived fact, such as a short list of the “best” browsers to support. The second is based on the idea—the principle—that every site should work on every platform you can reasonably manage, given your resources.

For another example, remember when we stopped asking what resolutions we should be targeting and switched over to responsive design? Yeah, it’s like that. Imagine if we were still endlessly chasing “the ideal resolution”.

What about “big images sell more”? It’s a trend, certainly, but it is not a fact you can always depend on. It might be better to say, “visually arresting design sells more”. In this way, you do not limit yourself to using big images everywhere.

Accept the Fact That Facts Change, and Constantly Double-Check Your Assumptions

With your principles in place as a foundation for your design process, you’re free to follow the facts wherever they might lead you, without fear. However, that freedom comes with a responsibility: you pretty much have to be constantly double checking your information and your assumptions. That doesn’t mean you have to constantly change how you do things, just that you need to keep double checking.

Assumptions, I have found, go hand in hand with dogma as people tend to cling to things that sound true as tightly as they do to anything that actually is true. I’ve done it myself, and it’s always embarrassing when you figure that out later. Check your data, check your facts (there’s a difference, these days), and double-check your assumptions.

The First Changes I’d Make

If you ever find yourself saying anything like, “X framework/CMS/whatever is the best one out there.”, that’s it. Those are the first things I’d check. You may even be right, but you likely won’t be right forever. And even then, whatever you think is “the best thing” may not be “the best thing for the job”.

Check your data, check your facts (there’s a difference, these days), and double-check your assumptions

Then I’d double check any deeply held beliefs you might have about “Users”. Oh, Psychology 101 isn’t going anywhere, but the way we interpret our knowledge of human nature changes over time. Again, check your data, do your research. See what users really do.

Lastly—and I know this isn’t about web design as such—this assumption that logos are better if you drain all personality from them. Plainer is not necessarily better. (Look, I know there were problems with the old Slack logo mark, but this swastika-made-of-phalli thing they have now is not the answer.)


Featured image via Unsplash

Add Realistic Chalk and Sketch Lettering Effects with Sketch’it – only $5!

p img {display:inline-block; margin-right:10px;}
.alignleft {float:left;}
p.showcase {clear:both;}
body#browserfriendly p, body#podcast p, div#emailbody p{margin:0;}

Privacy Settings
We use cookies to enhance your experience while using our website. If you are using our Services via a browser you can restrict, block or remove cookies through your web browser settings. We also use content and scripts from third parties that may use tracking technologies. You can selectively provide your consent below to allow such third party embeds. For complete information about the cookies we use, data we collect and how we process them, please check our Privacy Policy
Consent to display content from Youtube
Consent to display content from Vimeo
Google Maps
Consent to display content from Google