The current symfony.com web site was created before the release of Symfony 2.0 back in July 2011. Although the code is continuously updated to the most recent stable version (Symfony 3.3 at the time of writing this blog post), the application is showing its age in some parts. That's why we decided to revamp its front-end simplifying the templates and managing the web assets differently.

Our front-end needs are simple, so we use a pretty traditional setup based on Bootstrap 3 and a bunch of SCSS and JavaScript files. This worked well for us at the beginning, but it was becoming harder and harder to maintain lately.

The refactoring process took us almost two weeks and involved 50 commits changing 219 files (mostly .html.twig and .scss). We added or changed 6,209 lines of code and removed 10,291 lines. In this article, we explain some of the most relevant changes made during the refactorization.

New asset organization

Previously, we had tens of small SCSS files divided by their purpose: code.scss, typography.scss, forms.scss, etc. Besides making it hard to reuse styles, this approach complicates maintenance because it's hard to find all the styles involved in the design of a given page element.

This is a typical developer error: splitting something into lots of smaller pieces believing that this "modular" design is better, but ending up with a hard to maintain mess.

Now we define all the common styles in a big app.scss file and we have dedicated files for pages with special needs: home.scss, download.scss, etc. This makes the design massively simpler to maintain and helps us creating a more consistent design, because it's easier to reuse the same styles for different elements.

New design philosophy

The previous design was "Desktop first" and the new one is "Mobile first", which is something that we wanted to change since a long time ago. Any feature is now designed for and tested on smartphones first, and then we adjust things for larger devices if needed.

The result is that symfony.com contents now adapt nicely to any device. For example, the Symfony Roadmap page, where you can find information about the current and upcoming Symfony versions, now shows a vertical roadmap on smartphones and a horizontal roadmap on larger devices. See the before/after comparison of this page:

In order to avoid complicating the design too much, we decided to define just two responsive breakpoints: 768px for tablets and small desktops and 992px for the rest of devices.

New CSS styles

Previously, we didn't use any specific CSS methodology and most of our selectors relied on nested HTML id attributes (e.g. #p-7-2.post #comments #add-comment). The new design uses HTML class attributes exclusively and it's based on the BEM methodology. We don't apply BEM strictly because it can rapidly become too verbose, but BEM has helped us creating a more modular and easier to maintain design.

Another nice improvement was including third-party dependencies in a more granular fashion. Instead of including the entire Bootstrap 3 framework, we now pick the exact Bootstrap files that we need:

// app.scss
@import "~bootstrap-sass/assets/stylesheets/bootstrap/variables";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/mixins";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/normalize";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/grid";
// ...

The last change that allowed us to simplify styles a lot are the utility CSS classes that set the margin properties (e.g. .m-t-0 means margin-top: 0, .m-b-15 means margin-bottom: 15px, etc.)

Although they are a bit controversial and some people think that they can bloat your CSS, in our case we covered all our needs with just 10 utility classes, which in turn saved us lots of useless custom CSS classes that only set margins or paddings. These utility classes are coming to Bootstrap 4 too.

New workflow

At the beginning, we used Assetic to manage symfony.com assets. However, a few months ago, we removed it and started the transition to JavaScript-based asset management. As most non-JavaScript developers, we were confused by the amount of tools available, but at the end we settled on using Webpack.

Webpack is a nice tool to bundle your styles, scripts and images, process them and generate the final CSS and JavaScript files. However, at first Webpack is tough to grasp. Luckily, we had an ally: Ryan Weaver. During the past months, Ryan has been secretly working on a new JavaScript tool to manage web assets.

This new tool, called Webpack Encore, is a simpler way to integrate Webpack into your application. It wraps Webpack, giving you a clean & powerful API for bundling JavaScript modules, pre-processing CSS & JS and compiling and minifying assets.

We've been using this tool in production on symfony.com for the past couple of months and I must say that it's a delight to use. Moreover, this new tool will become the officially recommended way to manage assets on Symfony applications. Do you want to use it in your own projects? You won't have to wait much longer because it will be published this week.

New Twig templates

The previous Twig templates were pretty good, but we made some changes to them to simplify things using modern Twig features. These are some of the tricks we used and which you can use in your own projects too:

Null coalesce operator: introduced in Twig 1.28, it provides the same ?? operator as defined by PHP 7. It's a nice and concise replacement of the default filter:

{% set version = version_label ?? version_number ?? 'current' %}

{# equivalent to: #}
{% set version = version_label|default(version_number)|default('current') %}
{# also equivalent to: #}
{% set version = version_label is defined ? version_label : ... %}

Don't split templates into lots of fragments: splitting templates into tiny fragments and using include() to include them in the template can hurt performance. It also complicates maintenance, because it's harder to find where the contents are defined.

Create fragments only when some part of a template is truly reused in several templates. When including fragments, prefer the include() function to the include tag and always use the Twig namespace syntax, which is faster than the traditional bundle syntax:

{# the recommended way to include template fragments #}
{{ include('@App/blog/_list_comments.html.twig') }}

{# this bundle notation makes the application slower #}
{{ include('AppBundle:blog:_list_comments.html.twig') }}

Check for block existence: another feature added in Twig 1.28 is the support of is defined operator for blocks, which is useful to check for their existence in highly dynamic templates:

{% if block('intro') is defined %}
    <section>
        {{ block('intro') }}
    </section>
{% endblock %}

Define custom Twig namespaces: during the redesign, we replaced a custom icon font with proper SVG files for each icon. Referring to those files in templates is boring (e.g. images/icons/arrow.svg, bundles/blog/images/icons/arrow.svg) so we used custom Twig namespaces to store all icons under the icons namespace and embed them using the source() Twig function:

{# Twig namespaces create concise and beautiful templates #}
<i class="icon">{{ source('@icons/arrow.svg') }}</i>

Don't care about white spaces in HTML code: our work as developers is to create maintainable Twig templates, not to generate perfect looking HTML code. HTML is consumed by browsers not users, and it's mangled, minified and compressed before delivering it to the browser, so never mind about it:

{# this is beautiful and easy to maintain #}
<li class="{{ current == item.slug ? 'selected' }}" ...>

{# this is a mess and complicates everything for no good reason #}
<li{% if current == item.slug %} class="selected"{% endif %} ...>

The result

Combining all the changes and techniques explained above, the result of the refactorization was amazing. The symfony.com web site looks and feels the same, but all the design issues are gone, the site is fully responsive and "mobile first", and the performance has improved dramatically: before, every symfony.com page downloaded a 194KB app.css file (before gzipping it); now, the common app.css file weights just 59KB, a whopping 70% decrease!

Although the purpose of the refactoring wasn't to change the visual design of the site, we took this opportunity to make some minor changes, especially on the documentation section. For example, notes, tips and warnings now are easier to recognize:

Published in #Other #Symfony