Author: Steve Rackham, Software Engineer
At Mast, we've come a long way in terms of our site's design, while ensuring seamless transitions for our clients. A full site redesign was one of our biggest challenges as we launched the Mast Platform, and we handled it while prioritising stability and user experience. This article outlines the approach we took to conduct our in-house redesign and the challenges we faced (yet overcame!), hopefully providing some useful nuggets of information for others looking to do the same.
The Early Days: Bootstrap-Based Design
Initially, our site was developed by a single team member, meaning resources were constrained, and design wasn't the top priority over core functionality. We relied on out-of-the-box Bootstrap for both design and UI, which served us well at first but eventually limited our branding and user experience goals. As Mast grew, it became clear we needed a unique look and a more refined user experience to take it to the next level.
The Transition: From Bootstrap to Tailwind
Our redesign journey began with our product team collaborating closely with a designer to create a fresh look for the site. Once the new visuals were ready, our engineering team focused on a smooth implementation with three key goals:
- Zero downtime during the transition.
- No confusing user experience during the switch.
- Full control over when and how to launch the new design.
We decided to use Tailwind CSS for styling. Although Tailwind can be divisive, it met our needs perfectly, thanks to its utility-based approach that streamlined our development process. On top of this, Tailwind does a lot of heavy lifting to ensure the package we deliver to users is as minimal as possible without needing a bunch of configuration – no need to optimise PurgeCSS or similar, Tailwind handles all of that.
On the whole, our Tailwind configuration was pretty standard; we defined the files in which Tailwind classes could be used, and a colour palette to fit our designs. However, there are a couple of options that we chose, without which our integration wouldn’t have come together; finding and choosing these options was a matter of overcoming various challenges.
Overcoming Challenges: Prefixing and Specificity prefix
One of the key challenges was managing conflicts between Bootstrap and Tailwind. We used Tailwind’s prefix option, setting it to u-, to prevent clashes between utility classes like container. This simple adjustment allowed us to safely use both frameworks without any unexpected change in functionality.
important
The second significant configuration we used was important, specifically the Selector strategy for this. The option becomes slightly obtuse – it’s typically used to tell Tailwind that all its styles should be marked as !important – but suited our requirements perfectly. By providing a class name or ID to important, Tailwind styles will only apply if they are a child of the provided selector. This was our toggle: a switch that allowed us to use Tailwind styles or ignore them entirely. Now, our redesign was truly iterative - nothing visible until we chose to turn on this feature for users.
Flawless Execution with Feature Flags
To have this work, we placed our Tailwind work behind a feature flag. We use the wonderful Flipper to manage our feature flags.
Using the Flipper feature flag, we had complete control over the deployment. A simple toggle in the code controlled whether users saw the old or new design, making testing and demos straightforward and ensured a smooth switch when we went live, and the option to roll it back should something have gone wrong.
In practice, this looked like the following:
Set the important option to '.new-design' and everything’s up and running. With the feature flag on, Tailwind is applied, with it off, it’s not used at all.
It’s worth noting a couple of considerations here, though they proved pretty insignificant for us during the redesign:
- Using a selector strategy, the specificity of Tailwind is increased, as selectors become, for example, .new-design .u-container rather than simply .container.
- Should you need to use Tailwind on the top level element or higher (i.e. the body tag in our example above), you’ll be out of luck - the Tailwind classes have to be a child of the element on which the selector is applied.
Managing HTML Markup Changes with Rails Variants
At this point, we were up and running with Tailwind, but we hit another barrier: what would happen when the structure and HTML markup of a page changed from pre-design to redesign?
Fortunately, Rails – being the magical framework it is – had us covered here: enter variants. In a nutshell, by providing a “variant” you can direct Rails to render a different template to suit your requirements, allowing us to have diverging templates for legacy and redesigned views. An example will illustrate this well:
- home.html.erb for the legacy Bootstrap design
- home.html+new_design.erb for the new Tailwind design
Add a before_action to our application_controller and we had a feature that was as straightforward to toggle as Tailwind:
This setup allowed us to toggle between the old and new designs easily, keeping everything hidden until we were ready to launch.
Key Takeaways
- Tailwind CSS provided flexibility and reduced styling complexity, allowing for a custom design.
- Prefixing and specificity strategies ensured no conflicts with Bootstrap.
- Rails variants helped manage different HTML structures during the transition.
- Feature flags offered precise control over the launch, enabling safe testing and instant rollbacks.
This methodical approach to our redesign ensured a strong user experience and gave us the fine-grained control we needed to confidently present Mast’s new look to the world.
Photo by Hal Gatewood on Unsplash