Integrating the APEX Theme Roller in FOEX 4.0

Background

Since the release of APEX 5.0 it has been super easy to style your APEX application to meet corporate or personal taste requirements using the Theme Roller. However, prior to APEX 5.0 it was challenging for regular developers to customize APEX themes. Prior to v4.0, users of our framework have been in a similar position, it was really hard to customize a Sencha Ext JS theme, even more so than APEX.

To try and combat this issue in previous years we added more theme options and different colour variations. We also provided a theme customization service to create a theme that matched your corporate requirements. This was an expensive exercise, especially if it was simply about changing colours, but it still didn’t solve a very basic issue: our users simply had to write custom CSS when they wanted even the most basic change. We had to do better….

So it made sense that we somehow hook into Theme Roller to give developers the ability to customize the FOEX theme at the same time you customize the Universal theme (since you’ll most likely be using regular APEX components/pages in your FOEX application). The purpose of this blog post is to explain exactly how we integrated FOEX 4.0 with the APEX Theme Roller:

Theme Roller Behaviour

Within APEX Theme Roller, you can change the entire look of your application by making just a few colour changes at the global level. Changes made at the global level will adjust all colours below with the colour mixing rules e.g. lighten/darken by 50%. You can then make changes at a very granular level targeting specific region types by adjusting the one or more of the available colour settings.

There is actually a fair bit of code behind the APEX Theme Roller – it is essentially a control panel on top of a LESS compiler written in Javascript. If you have ever dealt with front-end web programming or UX, you most likely have heard about CSS preprocessors. These tools are supposed to simplify the creation of CSS rules programatically; they are basically scripting languages that extend CSS and once compiled, their output is readable CSS. The two most common preprocessors nowadays are SASS and LESS. The set of LESS rules which will be compiled by Theme Roller can be found in your Theme Styles under the Theme Roller Attributes.

Every time you change an attribute in the Theme Roller, the new set of values is run through the LESS compiler with these rules and new CSS rules are generated. This happens completely on the front-end i.e. in the browser. If you close the Theme Roller without saving, the rules are no longer applied and your application will revert to the current saved set of rules. If you set choose “Save” or “Set as Current” then on close of the theme roller your page will be reloaded and the new theme style changes will be persisted.

Theme Roller Integration with FOEX

All you need to do is add a LESS file to your theme, that’s it. The APEX team did a great job making it super easy to extend. Except writing the correct LESS rules requires a little know how though… So let’s look at where we add it and how we go about structuring and writing our LESS rules.

If you check your Theme Styles -> Theme Roller attributes in a FOEX v4 application, you will see there are two less files input there. The first one is the APEX’s Vita theme which handles all the APEX rules. The second one, FoexVita.less handles all the FOEX rules.

It isn’t necessary to have two files. In our case we required that both APEX and FOEX styles must work together. We could have copied the APEX rules into our file and include only that one but we decided to better split it up into separate files. The APEX files may be changed by the APEX team and we may not know about the change (and in that case we would need to update our file, of course).

One of the caveats we encountered in this 2-file-situation were the groups. Let’s look what the groups are, first:

Groups

Groups are the sets of attributes (of the application or regions) that belong together. They are all defined as comments in the less file in a JSON object:

"groups": [
   {
      "name": "UTR.LESS.GLOBAL_COLORS",
      "common": true,
      "sequence": 1
   },
   ...
]

You can see every group has a name, a common key and sequence.

  • name – can be a common string, it will appear in the Theme Roller as the group’s header.
  • common – specifies whether this group will be visible when the theme roller is in (let’s call it) ‘simple mode’, i.e. displaying fewer options.
  • sequence – specifies the order of the groups.

It may be worth mentioning that there is currently a sorting bug in the less.js file that APEX uses and the sequence does not work correctly in every browser as it uses this function to sort the array of groups:

less.groups.sort(function( a, b ){
   return ( a.sequence || 9999 ) > ( b.sequence || 9999 );
});

Array.protototype.sort should return a numeric value and the above function returns a boolean. The less javascript object sits on the window, so you may want to create a setter and a getter for it and its compile function and modify the sorting algorithm to return a number:

less.groups.sort(function( a, b ){
   return ( a.sequence || 9999 ) - ( b.sequence || 9999 );
});

The caveat with the groups and the 2-file-situation I mentioned above is that it is not simple to declare the groups in two files without creating more javascript overrides. The latest declared JSON simply overwrites the previous JSON’s and creates only the groups defined in it. That’s why we copied the groups from Vita.less and added them to our less file. This is something that is not very flexible (groups may be changed by the APEX team as well) but is something we can live with for now and will check when providing FOEX certification for future APEX versions.

A side note: In theory, it is not necessary to declare a group at all. You could just declare a ‘public’ variable (i.e. a setting that you want to be displayed in the Theme Roller) and set its group key to an nonexistent group. This group will be automatically created for you, but when using this method you cannot set its sequence and common attributes.

Now that you have your groups set up, let’s take a look at the variables / settings.

Variables / Settings

Every setting you see in the Theme Roller is a less variable but not every less variable is visible in the Theme Roller. It sounds a bit confusing so let’s clear it up.

If you want a variable to be visible in the Theme Roller, you must declare it similarly to the groups, as JSON object in a comment:

/*
{
   "var"     : "@fx-panel-header-background-color",
   "name"    : "Background",
   "type"    : "color",
   "group"   : "FOEX Global",
   "subgroup": "Header"
}
*/
@fx-panel-header-background-color: @g_Accent-BG;

Every less variable must be prefixed with an ‘at’ symbol. So this variable’s name is @fx-panel-header-background-color (it specifies the color of all FOEX headers) and its initial value is inherited from the APEX Global Colors @g_Accent-BG variable. These are the properties to set:

  • var – the less variable name, must begin with ‘@’
  • name –  a string that is displayed when you hover the attribute’s selector over in the Theme Roller
  • type – defines what kind of value is expected, whether color or number
  • group – under which group this setting belongs, if no group with this name exists, one is created
  • subgroup – the text of this attribute, it appears on the line next to the setting

Less variables with the same group and subgroup will be displayed on the very same line in the Theme Roller, that’s why the ‘name’ is important. Otherwise the user cannot tell what the setting refers to.

If you set type: “number”, you can define the range as well:

/*
{
   "var"   : "@fx-button-border-radius",
   "name"  : "Button Border Radius",
   "type"  : "number",
   "units" : "px",
   "range" : {
      "min"       : 0,
      "max"       : 30,
      "increment" : 1
   },
   "group" : "FOEX Global"
}
*/
@fx-button-border-radius: 0px;

So these are the “visible” or “public” attributes / variables which appear to the user in the Theme Roller. You can naturally define your own helper variables that will not be displayed in the Theme Roller by simply omitting the JSON in the comment block:

@fx-text-contrast: 75%;

This variable exists only in your less file(s) and will not be visible to the end user. You can still use it in your rules and definitions, e.g.:

@fx-panel-header-text-color: contrast(@fx-panel-header-background-color, darken(@fx-panel-header-background-color, @fx-text-contrast), lighten(@fx-panel-header-background-color, @fx-text-contrast), @fx-text-threshold);

If you try out the Theme Roller, you can see that if you change the background color to a dark color, the text changes to light. If you change the background color to light, the text changes to dark. This is all handled by the less variables as the one above. Contrast, darken and lighten are built-in LESS functions. You can find all the functions in the LESS documentation.

Just for the sake of clarity, what the above rule is saying is:

  1. Create a color darker than the background by 75%
  2. Create a color lighter than the background by 75%
  3. Get the more contrasting color (with the specified threshold) and use that as the text color

Most of the FOEX (and APEX) variables are set in this way – to “inherit” from another variable. Sometimes one color is a bit darker, sometimes lighter, sometimes desaturated, but in the end they are all connected to a parent color. That enables us to “cascade down” the colors from the global settings.

You surely noticed how by changing a color in the Global Colors group all your application changes. That’s exactly the reason. FOEX Global inherits from Global Colors (notice that you can use the variables defined in the less other file which is excellent because the user can just modify the global colors and will get a nicely adjusted APEX+FOEX style), FOEX Grid inherits from FOEX Global, etc.

However, as soon as you change the setting for an attribute using the Theme Roller, this attribute will not inherit anymore. So changing manually the panel header color and then changing the FOEX Global colors will not propagate down to the panel header color anymore (as you have changed that manually already).

Once you have all your variables set up, you can use them in other variable declarations or in your CSS rules. We have picked every component’s rules that need to be overwritten and then we pass the appropriate variable, e.g.:

.x-panel-body-default {
   color: @fx-panel-body-text-color !important;
}

Wrapping Up

The changed settings in the Theme Roller can be retrieved programmatically in Javascript. You could call apex.utr.config() from the console (or from your scripts) which returns a JSON object with the changed variables, e.g.:

{
   "customCSS": "",
   "vars": {
      "@fx-form-field-background-color":"#942794"
   }
}

Note that these changed variables are:

  • the variables that differ from the less file’s default values (not from the saved/current style’s values)
  • are only the values that were changed by the user, the calculated (children / cascading) values are not included, these are evaluated automatically

This can be useful if you want to create many styles fast and you need to change the very same variables. You can then pass this JSON object to the Theme Roller’s JSON Configuration field in your Theme Styles, as you can see on the image (the config pictured is FOEX Style 1, i.e. the dark style). You could also pass your modified object to apex.utr.config() to see the changes right away, your styles will get recompiled.

Final Words

In case you were wondering….

Why do the FOEX settings inherit from the Global Colours only and not from the other groups (such as Forms or Interactive Reports, etc.).

  • This is because FOEX components usually consist of a much more complicated structure / more colours and it would be really difficult to adjust these APEX settings to the FOEX components.

What this small color change is when you open the Theme Roller – your FOEX application changes color as soon as that happens.

  • That is because we have defined these cascading rules. As soon as the Theme Roller is opened, the rules are recalculated and the result is shown. Because our less rules are not exactly the same as the default ExtJS Triton Theme, the color change occurs.

Hopefully that gives you enough information to integrate with APEX Theme Roller. The APEX team did a great job making this extensible, so if you are developing 3rd party plugins for APEX you should consider providing a LESS file that can be included in the APEX Theme Style(s) so that your plugin can be easily styled.

So head over to your FOEX trial workspace, or sign up for a new one and check out the Theme roller integration in action. We’re eager to receive your feedback!

Credits

We must give credit to Vincent Morneau and his blog post Customizing Theme Roller, which started our approach and we further built upon with our own R&D.

 

Add your comment

Your email address will not be published. Required fields are marked *