Code Guidelines

Good styling code, like any good code, is reusable, DRY, and semantic.

Overview

Most of us don’t write much CSS. HQ uses Bootstrap 5.3, which gives us a consistent and fairly comprehensive set of styles. Most of the "styling" we do is picking the appropriate Bootstrap (or HQ) classes and then troubleshooting anything unexpected.

We use SASS / SCSS to write styles. SASS is a powerful extension of CSS that allows for more maintainable and reusable styling by supporting variables, mixins, and so much more. SCSS is a flavor of SASS with a syntax closer to CSS syntax where the use of semicolons and braces are mandatory. Since Bootstrap 5 uses the SCSS flavor of SASS, we also have chosen to use SCSS.

Reusable CSS is good for the same reasons reusable code is good everywhere else. On the front end, reusable code is also correlated with visual consistency and generally better UX. To that end, when creating a new page or section:

  • It should look very similar to related HQ pages.
  • It should look fairly similar to other HQ pages.
  • It should look somewhat like the rest of the internet.

Bootstrap takes care of a lot this: it gives us a consistent framework so our pages look like each other, and that framework is designed to resemble the rest of the internet.

Good styling is semantic, because that makes it easier for other developers to reuse. Ideally, a visual designer decides what an error looks like, or what a CRUD page looks like, and then developers only need to determine that they're displaying an error, or creating a CRUD page, and the look and feel is taken care of.

Classes like .bump-down { margin-top: 10px; } are problematic because it isn't clear when and why they should be used, so they get applied inconsistently, and we end up with a site that looks a little bit...off...but it isn't obvious why. Bootstrap is a good example of a largely semantic system: classes have names like .btn-danger rather than .btn-red, to make it clear why you should use a particular class.

Style Hierarchy

Most styling should live in external SCSS files.

Most HQ-specific styling lives in external SCSS files, typically in corehq/apps/hqwebapp/static/hqwebapp/scss.

App-specific styling can live in corehq/apps/style/static/APPNAME/scss and then be included with <link> tags in the appropriate template.

Some pages use in-page <style> blocks. This isn't ideal for two reasons:

  • In-page styles can't be reused by other pages - but for the sake of a consistent experience, most styling shouldn't be specific to a single page.
  • In-page styles can't use SCSS, so they tend to be less DRY and has to hard-code values like colors and dimensions, rather than using the standard values stored in SCSS.

Inline styling is generally a bad idea for the same reasons as in-page styling: one-off styling isn't good for consistency and often isn't semantic.

Handling Z-Index

Disorganized z-indices lead to highly visible bugs.

Z-index gives you control over the stacking order of elements. The challenge is that it acts like a global variable and, like any global variable, gets hard to keep track of. HQ deals with this as best as it can, by declaring numeric z-index values as variables in variables.scss and using the variables in other SCSS files. This isn't perfect, but it at least gives us one place to define HQ-specific absolute z-index values - which also need to play nicely with Bootstrap's z-index list.

Most z-index issues arise from not having a good handle on all of the different values used in an application, but there are a few other complexities that occasionally cause problems:

  • Stacking levels: Z index isn't the only aspect of element stacking. Stacking is also affected by element positioning and floating. "Natural" stacking order, from top to bottom:
    • Positive z-index
    • z-index auto or 0
    • Inline elements
    • Floated elements
    • Block-level elements
    • Negative z-index
    • Background
  • Stacking context: Z-index isn't strictly global, it acts within a stacking context. There's a global context but may also be sub-contexts within that. The most common of these is that an element with a non-auto z-index creates a stacking context. This is intuitive: if you set a modal to z-index: 9999, you don’t have to set the z-index on all of its children to 1000, they just come along for the ride. But there are other properties that also create new stacking contexts where you might not expect it. position: fixed is one, translucent elements is another. The others are pretty obscure.