Learn the Vanilla and Flavouring Pattern
(Source/Credits: https://dev.to/giteden/learn-the-vanilla-and-flavouring-pattern-4c71)
Originally written by Uri Kutner on "Bits and Pieces" Can you write code that is both reusa...
Originally written by Uri Kutner on "Bits and Pieces"
Can you write code that is both reusable and specific enough for a beautiful design?
This pattern tries to tackle a difficult problem in coding: how do you make code that is generic and stable, but at the same time replaceable and specific?
Reusable specific code
In a top-down approach, one starts with a result in mind, and quickly iterate over the code until it produces the desired result. You may know this as Test Driven Development, but it works the same with you as a tester.
A button from Bit’s reusable components platform
Pretty straightforward. The button is a simple component with a border, border-radius and a color. You can even give it a small transformation so it would react when you click on it. So far so good.
{% gist https://gist.github.com/KutnerUri/fcef9f744e8efa7b949a61fb9fd9dee6 %}
Unbeknownst to the button, in a different part of the code, another button is needed. One that is bolder, fuller, and more fitting to direct the user’s attention to whatever you want at the time. It has a purple background, no border, and white text.
How does one make a new button? Option 0: Create a new button from scratch
Yes this is a thing. Developers reinvent the wheel on a daily basis. Some of my best friends are dead Dependency Injection frameworks that people just have to write for themselves. Custom specific code is easy to tailor and fits most corners easily. It even simplifies your whole codebase by tackling a piece of existence all by itself. Of course it also means that you would have to update it manually for every single bug in the buttonshpere. Good luck with that.
Option 1: override using styles
As our button receives a classname, we could easily override the styles we’d like, and get the result we wanted. However, this method is flawed — the new button still receives all the base styles from the original button, so it will inherit its future changes as well. For example, when we make the original button bold, the new button would also get this style. To keep the new button consistent, we would have to review it and override the new styles. This can be time consuming and even difficult with some styles.
This violates the Open Close Principle. The component is open for extensions, but it is defiantly not closed to modifications. Such violation to the Pure base class can sometimes be worse than you would expect:
yikes! I haven’t even edited this, it was like that when I found it
Option 2: put a flag on it
UI Components are, basically, functions. You could pass parameters, and make logic around it. This is the structure in JSX, but it could work just as well in other formats.
<Button invert={true} onClick={ () => this.handleCatGifs()}/>
It makes sense in the first glance, until you realize it violates the Inversion of Control Principle. You would be trapped in a maintenance hell really fast.
huh.. switch-case anyone?
And trust me, it is even worse if you have a bug that only effects some buttons and not others.
How, then, does one tackle this issue?
Enter the Vanilla and Flavouring pattern 🍨🍨🍦
Create one ‘base class’ called Vanilla. This base houses all the generic functionality. You can put anything in there, as long as any logic and even styles must always apply, in all situation.
{% gist https://gist.github.com/KutnerUri/fcef9f744e8efa7b949a61fb9fd9dee6 %}
Create a new decorating Flavor for each new instance of look. Flavors may not even depend directly on the Vanilla, just provide their styles. Ideally, you should create a flavor to match each style in your style guide.
{% gist https://gist.github.com/KutnerUri/9f63df71c96e5679d5a6189ff53773dc %}
Keep your Flavors as physically (or folderally) close to the original Vanilla base. When it changes, you would want to make sure they adapt perfectly.
When you want to change the look of a certain element, replace it with another Flavor. All looks must have their own flavor! Rouge styles are difficult to keep track of, and they indicate you are drifting away from your Style Guide.
When Flavours are no longer used, delete them! (…or keep them in an archive, if you get nostalgic). Ideally, when the Style Guide deprecates a theme, the code counterpart for this theme should be deprecated as well.
And that’s it. Vanilla keeps getting better, and new flavors come out every month. It’s a wonderful time to be a button!
{% gist https://gist.github.com/KutnerUri/cc9b2fbf43201210a134df913530d41c %}
What did we gain?
A well-established core responsible for the Fit and Function of the component. A cheap decoration layer made out of many reusable variations, responsible for the Form of the component.
You could even add new behavior and animations to Vanilla later on!
ever thought that onClick on return a Promise?
In a wider lens…
Could your code benefit for the Vanilla and Flavour pattern? Could you have already have been using it without a taste metaphor? Let me know what you think in the comments below.
Relevant tips:
-
Never give vanilla any styles! especially sizes (margins, paddings, positioning), color (font, border, background, shadow), fonts, decorations.
-
Give a meaningful and specific name to your flavor. Focus on their purpose, and consider they would be replaced by very similar flavors, so don’t be too abstract. At Bit, we prefix flavors by the current design system. It looks like Neon signs right now so we call them Neon buttons.
-
Be brave. Make a code fork of your Vanilla and start a new base class with new, exciting different functionality.
-
Compose styles together by passing flavored instances together. eg., a vanilla confirmation modal would use a vanilla button, but the NeonConfirmationModal would pass styles and NeonButton to its Vanilla.
That’s all for now. Thanks for reading, and feel free to comment and ask anything! I’d be happy to chat :)
Comments section
pencillr
•May 1, 2024
Definitely something I will apply in my daily work. Thanks!
alicia1990jones
•May 1, 2024
Another great post.