Cx Walkthrough Part 4: CSS

Marko Stijak
Codaxy
Published in
6 min readNov 25, 2016

--

In the previous posts, we learned how to get the application up and running. This post will be on how to modify the appearance of Cx widgets using Sass/SCSS.

Sass is the most mature, stable, and powerful professional grade CSS extension language in the world.

According to the State of the JavaScript 2016, 80% of the survey participants have already used Sass/SCSS, and this is by far the most popular solution for authoring CSS.

At Codaxy, we use SCSS in all of our projects. SCSS stands for Sassy CSS, a flavor of Sass which uses CSS-like (bracket based) syntax instead of indentation.

The SCSS files are compiled to CSS using node-sass and webpack loaders. Webpack also enables hot updates — whenever a .scss file change is detected, the page is automatically updated.

The main entry point is the app/index.scss file. This file is used to add default (reset) styles and import Cx (cx.scss), route (route/index.scss), and layout (layout/index.scss) styles.

The interesting piece is the HTML related block:

html {
height: 100%;
font-family: -apple-system, BlinkMacSystemFont,
"Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell",
"Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
font-size: 14px;
@media screen and (min-width: 1024px) {
font-size: 16px;
}
}

Note that height is set to 100% to occupy the whole screen. The app uses system font, which is different for each platform. Default font-size is 14px (mobile). On larger screens, font-size is increased to 16px. This is very important to know because some padding and margin values are expressed in rem units and, therefore, depend on the base font-size.

Cx CSS (cx.scss)

This file is used to import Cx styles. Cx framework contains many widgets. We have an option to load all widgets or only the pieces we want. Including CSS for all widgets is a simpler scenario and here we’ll cover how to include only a subset of available widgets.

//override Cx defaults
$cx-default-box-line-height: 1.25em;
$cx-default-box-font-size: inherit;
$cx-default-box-padding: 0.5rem;
$cx-default-input-tag-spacing: 0.2rem;

//import variables, mixins, but leave out any CSS
$cx-include-all: false;
@import "~cx/index";

First, set some defaults for Cx widgets. The first two lines are very important. The default input line-height is set to 1.25em and font-size to inherit. That means that the height of input fields will be controlled by the current font-size, and it will be calculated as 1.25 * font-size + padding-top/bottom + border-width-top/bottom. Some browsers do not respect line-height set on input elements, so Cx explicitly assigns a height to enable identical appearance across browsers.

Next, $cx-include-all is set to false which prevents loading of CSS for all widgets. Instead, only variables and mixins are loaded.

state-style-maps

Styling widgets is a complicated task. Even for a simple text field, there are many different things that need to be addressed, such as borders, background, color, padding, etc. There are also different states, e.g. hover, focus or error. If a variable is used for each property and each state, the number of variables would be huge. Instead, Cx uses the concept of state-style-maps which are basically two-level Sass maps. The first level is the dictionary of states, where the value of each state is a dictionary of CSS rules.

$cx-input-state-style-map: cx-merge-state-style-maps($cx-input-state-style-map, (
default: (
border-width: 0 0 1px 0,
padding: 0 0 0.5rem 0,
background-color: transparent
),
focus: (
box-shadow: null
),
error-focus: (
box-shadow: null
)
));

This snippet should be read as follows: Let’s use standard Cx input appearance, but use only bottom border of 1px and bottom padding of 0.5rem, with a transparent background. When inputs are focused, do not add box-shadow, even if the input is in the error state.

Border width and padding are tweaked to enable Material Design like inputs. Box shadows are removed as they don’t look good when the borders are missing.

From the screenshot above, you can see that all form inputs are now styled in the same way. That’s because the default style was changed. Also, notice that 0.5rem padding is equal to 7px for smaller devices and 8px for desktop size screens.

Next, as we didn’t include all Cx widgets by default, we need to include styles only for the widgets we use in the application by calling widget mixins:

//include only CSS we need
@include cx-label;
@include cx-textfield;
@include cx-datefield;
@include cx-calendar;
@include cx-lookupfield;
@include cx-overlay;
@include cx-dropdown;
@include cx-labelstoplayout;
@include cx-tooltip;

SearchField.scss

The SearchField widget appears in the header and allows users to enter search queries. The header has a distinct visual style, and a standard text field does not fit in nicely. The search field should be as simple as possible and have a different placeholder color.

To achieve that, we’re going to subclass the text field widget. This gives us an opportunity to redefine the appearance completely.

$searchfield-state-style-map: cx-merge-state-style-maps(
$cx-input-state-style-map, (
default: (
border-width: 0,
background: transparent,
color: white,
font-size: 1.5rem,
padding: 0.5em 0
)
)
);

@include cx-textfield('searchfield', $searchfield-state-style-map,
$placeholder: (
color: rgba(255, 255, 255, 0.5)
)
);

.cxb-searchfield {
flex: 1;
width: auto;
}

This is achieved by creating a new state-style-map specific to the search field and passing it to the cx-textfield mixin. We now have a completely new widget type with CSS for all states. Using JavaScript, we may add more functionality to the widget, although in this case, this is not required.

import {TextField} from ‘cx/ui/form/TextField’; 
export class SearchField extends TextField { } SearchField.prototype.baseClass = ‘searchfield’;

BESM

Cx uses the Block Element State Mod convention, which is inspired by the original BEM convention and extended to better suit the needs of a widget library like Cx.

All generated CSS classes are prefixed. By default, blocks use cxb-, elements use cxe-, states use cxs-, and mods use cxm- prefix.

Let’s see how this works on the example of the HTML code generated by the ColorField widget.

<div class="cxb-colorfield cxs-edit-mode cxs-visited">
<input class="cxe-colorfield-input"
type="text"
id="fld-1859"
value="#f88"
>
<div class="cxe-colorfield-tool">
<div style="background-color: rgba(255, 136, 136, 1);"></div>
</div>
</div>

Each widget starts with a block, which contains elements and state flags. In this example, there is a cxb-colorfield block, which contains cxe-colorfield-input and cxe-colorfield-tool elements. The widget is in the edit mode and it has already been visited by the user, which is denoted with two additional CSS classes: cxs-edit-mode and cxs-visited.

To understand mod, let’s see an example from the Cx documentation.

Mod is assigned using the mod attribute.

<Button mod=”primary”>Primary</Button>

As a result, an additional class is appended to the block element in the HTML output.

<button class=”cxb-button cxm-primary” 
type=”button”>
Primary
</button>

This additional class allows us to add rules which change the appearance of the widget. Mods should be used when changes relate to appearance, like changing colors, padding, font-size, border-radius or similar. When the change is very big, subclassing is a better option.

Conclusion

In this post, we learned how to use Sass variables and maps to tweak the appearance of Cx widgets. Cx allows us to import all the widget styles or manually pick and import only the styles that are actually needed. Finally, we saw how to subclass new widgets and learned about prefixes and BESM convention.

That’s all folks. I hope you enjoyed the series and that you’re going to use some of the tricks learned here in your own applications. Some topics were covered only briefly, so feel free to ask any question in the comments.

--

--