Packaging SCSS or CSS in an add-on
The add-on architecture of Vaadin enables you to easily create reusable
components and share them with the world. The add-on can be of various
types: widgets, themes, server side components etc. Most of them contain
Java code and classes, themes provide CSS and SCSS which can be used as
the application theme but how should package add-ons which are not
themes but still include CSS or SCSS? Let’s explore the options by
creating a fancy DangerButton
component (a native button which should
have a red background).
If we have an application (as opposed to an add-on) and want to create a red native button we have many options:
1. Use NativeButton with a style name and define the style in the application theme
NativeButton dangerButton = new NativeButton("Don’t click me");
dangerButton.addStyleName("danger");
In the application theme:
.danger {
background-color: red;
}
In our application we would of course encapsulate this in a
DangerButton
(extends NativeButton
) which always adds the “danger”
style name.
2. Use NativeButton with a style name and @Stylesheet to include the style
Do as above but create a custom css file which we place in the same folder as DangerButton.java and annotate DangerButton with @Stylesheet(“dangerbutton.css”). The @Stylesheet annotation will take care of publishing the css file and make the browser load it.
3. Create a custom connector and define the style inline
Create a custom component + connector by creating DangerButton extends
NativeButton
and DangerButtonConnector
extends NativeButtonConnector
. We can then manage to do this in the
connector e.g. by setting an inline style when creating the widget. The
upside of this is that it will work without a custom theme, the downside
is that tuning the exact red color becomes cumbersome as you need to
recompile the widget set (or use dev mode) for each update.
4. Include the style sheet in a widget set
Include the style sheet in a widget set by adding
<stylesheet src="dangerbutton-widgetset.css"/>
to the widget set
gwt.xml file and adding the css file to the “public” folder inside the
widget set folder (containing the gwt.xml file) so it will be included
in the compiled widget set.
5. Dynamically inject the styles
Use NativeButton with a style name and dynamically inject the CSS needed, e.g.
NativeButton dangerButton = new NativeButton("Don’t click me");
dangerButton.addStyleName("injectedDanger");
getPage().getStyles().add(".injectedDanger {background-color: red;}");
This approach does not require widget set compilation or a custom theme but does not support scss and injecting a lot of css this way can quickly add up to a lot of style tags in the page.
What about the add-on
Returning to add-on discussion, which of the options above are viable for an add-on? What should we really use for our DangerButton add-on to keep it simple and avoid possible future problems?
The first option which includes the application theme is not viable for an add-on as such except if we are fine with documenting that you should copy paste the css into your application theme.
Option 2 seems like a good option but the day we add images or other resources to the style sheet we will run into problems as @Stylesheet only publishes the css file and nothing else. Including other style sheets (even when they are published with @Stylesheet also) does not work as we do not know the actual URL they will be available from at runtime and including images will also cause issues as we have no easy way of making them available. Also we cannot use SCSS with this approach.
Option 3 and 4 with the custom widget set usually work out fine, you can even use SCSS for these (although it won’t be included in the application theme, e.g. prefixed correctly). However, if we want to keep things simple we do not want to make the add-on user compile widget set.
We also do not want to go with option 5 as it does not support SCSS and have potential issues when overused, especially in older Internet Explorers.
So, what about the add-on..?
A sometimes overlooked feature of Vaadin is the VAADIN folder used to
serve themes and widget sets to the browser. As a matter of fact all
files inside the VAADIN folder are published directly as-is to the web,
also when the VAADIN folder is in an add-on jar. So by placing our style
sheet inside a “/VAADIN/addons/dangerbutton/” folder it will be
available for the browser and any images or related style sheets will
also be available (and relative paths will work). The only question
remaining is how we can include the style sheet automatically for any
user of our add-on and this can be done using @StyleSheet
with the
special vaadin://
protocol, e.g.
@StyleSheet(“vaadin://addons/dangerbutton/styles.css”)
.
That does not work with SCSS
One of the nice features of SCSS is that the theme creator can define variables which you can use in your own scss file. If we are doing standalone compilation of the scss file to css we are not able to take advantage of these features. Also our styles will not be restricted to the application theme (using theme name prefixing) if it is not compiled at the same time as the application theme. Finally by including the styles in the application theme, all possible minifiers and similar can operate on the add-on styles also.
To take full advantage of SCSS we can, instead of using the
@Stylesheet
annotation to include a css file directly, define in the
add-on metadata that the addon jar contains a scss (or css) file which
should be included when compiling the application theme. This is done
with the Vaadin-Stylesheets
attribute, for instance for our
DangerButton
as:
Vaadin-Stylesheets: VAADIN/addons/dangerbutton/dangerbutton.scss
A small gotcha here is that the Vaadin-Stylesheet
attribute refers to
the full path inside the add-on jar (i.e. starts with /VAADIN) whereas
the /VAADIN part is included in the vaadin://
protocol when using
@Stylesheet
.
Now when you add the dangerbutton add-on jar to a project you will see that the addons.scss file gets automatically updated to include the dangerbutton css and it is compiled together with the rest of the application theme.