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 and992px
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 theBEM 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. Itwraps 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 thedefault
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_labelisdefined ? 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 theinclude
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 icon
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{% ifcurrent==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:
![]()