Instantly share code, notes, and snippets.

Embed
What would you like to do?
Kuali Student CSS Best Practices

Table of contents

  1. Principles

Principles

  1. Readability: Names of selectors must inherently describe the location of the corresponding source code.
  • Modularity: Styles should be grouped and isolated by their respective domain of use.
  • Low coupling: Styles should never depend on the structure or semantics of markup.

Project structure

The following folder structure should be the starting point for a Kuali project's styles.

styles/
  core/
  modules/
  vendor/
  main.less

Styles folder

The styles/ folder contains all style files for the project.

Core folder

The core/ folder contains all foundational and supportive code, including mixins, variables, and basic styles to be generally applied to HTML tags.

Modules folder

The modules/ folder contains the majority of the style code.

Vendor folder

The vendor/ folder contains all third-party style code.

Main file

The main.less file is the starting point for the styles. Its sole responsibility is to import all the other files in the core/ and modules/ folder in their proper order. No other file should contain an @import declaration.

Module naming methodology

All selector names are composed of four primary parts.

.<namespace>-<Component>-<element>--<modifier> {}

This syntax helps the following ways:

  • The hierarchy of a selector is easily understood and readable by the name alone.
  • Selector specificity is low, making it easier to predictably and successfully apply styles.
  • Presentation (CSS) and content (HTML) is better decoupled, giving greater flexibility to both.

Namespace

In order to prevent naming collisions of components across all Kuali applications, all selectors should be prefixed with a namespace. The name of the namespace should be the shortened name of the respective Kuali module in question or a derivative of it. Namespaces should be spelled using spinal-case (i.e. lowercase with hyphens delimitating words).

The following are examples of potential namespaces, with their translation:

  • ks: Kuali Student.
  • ksap: Kuali Student Academic Planner.
  • ksap-audit: A sub-module of KSAP dealing specifically with auditing features.

Every namespace is also represented as a sub-folder within the modules/ folder.

styles/
  core/
  modules/
|   ks/
|   ksap/
|   ksap-audit/
  vendor/
  main.less

Component

A component is any derivable part of the interface. It can be pulled out of its respective context and understood in isolation. Each component is placed in a unique file, under its respective namespace folder. Both a component and its file name should be spelled using PascalCase (i.e. capitalized words without delimiters). In this way, the class name .ks-Button inherently describes its respective file path (modules/ks/Button.less).

styles/
  core/
  modules/
    ks/
|     Button.less
    ksap/
    ksap-audit/
  vendor/
  main.less
// modules/ks/Button.less
.ks-Button {}
<button class="ks-Button"></button>

Element

An element is any descendant of a component. Elements are optional because not every component needs elements. Elements should be spelled using camelCase (i.e. capitalized words without delimiters, with the first letter lowercase). All element styles are placed in the same file as its respective component.

// modules/ksap/Search.less
.ksap-Search {}
.ksap-Search-button {}
.ksap-Search-input {}
<form class="ksap-Search">
  <input type="text" class="ksap-Search-input"/>
  <button class="ksap-Search-button"></button>
</form>

By reading the class names in the HTML, it's understood that the class .ksap-Search-button is only sensible as a descendant of the .ksap-Search component. Even if the component class won't include any styles, the component class must be represented in the markup.

The styles in .ksap-Search-button should not reimplement common button styles that would be in a more general namespace, like .ks-Button. Rather, .ksap-Search-button should only include styles specific to a button decedent of a .ksap-Search component. To take advantage of .ks-Button styles, that class should be appended to the markup as well.

<form class="ksap-Search">
  <input type="text" class="ksap-Search-input"/>
  <button class="ksap-Search-button ks-Button"></button>
</form>

In this way, <button> is both an element and a component.

Modifier

A modifier describes a state of a component or element. Modifiers should be spelled using camelCase (i.e. capitalized words without delimiters, with the first letter lowercase). All modifier styles are placed in the same file as its respective component or element. A modifier should not be declared in HTML without its respective component or element class also declared.

// modules/ks/Button.less
.ks-Button--small {}

// modules/ksap/Search.less
.ksap-Search-input--empty {}
<form class="ksap-Search">
  <input type="text" class="ksap-Search-input ksap-Search-input--empty"/>
  <button class="ksap-Search-button ks-Button ks-Button--small"></button>
</form>

In this example, the modifier .ksap-Search-input--empty declares styles for an empty state that's unique to that .ksap-Search-input element. Likewise, the modifier .ks-Button--small declares styles for a .ks-Button component that is of the small kind.

References

The aforementioned naming methodology is highly inspired from the work of Nicholas Gallager and his best practices as outlined for his SuitCSS project. Those practices are refinements to the BEM (Block Element Modifier) naming methodology. The OOCSS (Object-oriented CSS) and SMACSS (Scalable and Modular Architecture for CSS) methodologies may be worth further exploration, but initial impressions are unfavorable.

The reason I choose BEM over other methodologies comes down to this: it's less confusing than the other methods (i.e. SMACSS) but still provides us the good architecture we want (i.e. OOCSS) and with a recognisable terminology.

— Mark McDonnell, Maintainable CSS with BEM

Coding conventions

Whitespace

  • Use 2 spaces for indentation, never tabs.
  • Don't use more than one blank line as a separator.

Ordering

  • Within a code block, organize in the following order, alphabetizing within their respective groups:
  1. @variables
  • .mixins()
  • properties:
  • :pseudo-classes
  • ::pseudo-elements
  • --modifiers
  • -elements
  • Alter this default order only if it produces undesirable results.

Colors

  • Lowercase hex values: #fff over #FFF
  • Shorten hex values, if possible: #fff over #ffffff.

Properties

  • Always put a space after a property's colon: display: block;, not display:block;
  • End all properties with a semi-colon.
  • Place one property and corresponding value per line.

Nesting

  • Avoid nesting selectors.
  • If nesting, never nest more than 3 levels deep.
  • If nesting, prefer child selectors (parent > child) over descendant selectors (parent descendant), to better isolate cascading styles.
  • If nesting and styling a tag, prefer wildcard selectors (*) to particular element tags to reduce DOM coupling.

Selectors

  • No #ids, only .class-names, even if the class is guaranteed to be used only once.
  • Attribute selectors, like input[type="text"] should always wrap the attribute's value in double quotes. This is important to do in your own code as well for consistency and safety (see this blog post on unquoted attribute values that can lead to XSS attacks)

Units

  • Declare font-size at the lowest level possible. Use px sparingly. rem most likely for spacing.

Never

  • Beware of !important.
  • No inline styles.

[Should these be adopted?]

  • For multiple, comma-separated selectors, place each selector on its own line
  • Double quotes only, never single quotes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment