Element HTML and CSS guidelines

Jump to: navigation, search
Renderer consistency
Project state Specification
Tracker issue https://tracker.moodle.org/browse/MDL-45830
Discussion https://moodle.org/mod/forum/discuss.php?d=261202
Assignee Damyon, Sam

Note: This page is a work-in-progress. Feedback and suggested improvements are welcome. Please join the discussion on moodle.org or use the page comments.


This is a guide to writing HTML and CSS for elements within Moodle. In the future this will likely become the HTML and CSS guide for output in Moodle as we want to move elements. However at present we don't want to enforce this for all output, just new conversions to elements.

Why have a strict guide

At the time of writing this CSS within Moodle (2.7) themes is in a poor state and has been for a long time. Without previously having a guide we were finding that:

  • As there was no formal guide to creating id's or classes, each developer does their own thing.
  • As there was no formal guide on how to write CSS there were numerous techniques used when writing CSS and no consistency.
  • Majority of pages create unique output and require unique CSS, there is very little re-use of design.

While those points may not sound bad to you consider that in following stats from Moodle 2.7 themes:

  • Base theme contains 3802 rulesets with a total of 5800 styles.
  • Bootstrapbase theme contains 5170 rulesets with a total of 9362 styles.
  • Clean theme contains 5172 rulesets with a total of 9366 styles.
  • More theme contains 5177 rulesets with a total of 9063 styles.

You get the picture, several thousand selectors containing nearly 10000 styles.

For Moodle 2.8 we committed to introducing a element library to aid the reuse of design and documenting all aspects of output including a guide on writing CSS for Moodle. This is that guide.

Our goals

Its very important to understand what we are trying to achieve with this style guide and output in general.

Frontend framework agnostic 
We want theme designers to be able to apply present and future frontend frameworks to Moodle with minimal effort.
Consistent interfaces 
We want Moodle to have consistency throughout its interfaces, minimising the amount of frontend design and development that is required in coding and styling an interface.
Cleanly formatted HTML and CSS 
to minimise both development time and to make our renderers and CSS and more maintainable.

We plan to faciliate these with the following:

Elements 
Highly re-usable objects to produce HTML, these will be used consistently and relied upon by developers creating interfaces. By having these elements we create a consistent look, interface creation will be much quicker and theme designers through the re-use of a limited number of elements.
Element library 
A tool to show samples of each element, this can be used to view and test designs. It aids the developer by showing them the elements they can select from when creating an interface, and it aids designers by giving them a test area that doesn't require them to dig through every page in Moodle.
Documentation 
We will now have proper documentation on all aspects of output. Including this CSS style guide, a renderer best practices guide, and a guide to creating elements.

Where to put CSS in Moodle core

CSS can reside in a number of locations depending upon what you are styling.

Remember it doesn't matter how many CSS files we end up with, on a production site with Theme designer mode switched off (default) all CSS files will be combined and minimised into a single file that is served to the client. Excepting the case of IE where we must split the CSS into several smaller files to work around browser limitations.

CSS for elements

As we are moving to using elements you will find that CSS for elements is where most of the effort is going to go.

CSS for elements should reside within themes only, you will find that elements must be styled in all core base themes, this means at least Bootstrapbase and base.

CSS for elements should be located within either theme/themename/style/elements/elementname.css or theme/themename/less/elements/elementname.css

The actual CSS file should be named according to the element. As elements can be namespaced we need to ensure that that is reflected in the name of the file. For instance:

Element CSS location in base CSS location in bootstrapbase
\core\output\button style/elements/core_output_button.css less/moodle/elements/core_output_button.css
\core\output\navigation_bar style/elements/core_output_navigation_bar.css less/moodle/elements/core_output_navigation_bar.css
\mod_assign\output\user_submission style/elements/mod_assign_output_user_submission.css less/moodle/elements/mod_assign_output_user_submission.css

These will still need to be included in the theme, by which ever means is relevant. If you are using .css files like the base theme does then you will use:

// In your themes config.php
$THEME->sheets = array(
    'elements/core_output_button',
    'elements/core_output_navigation_bar',
    'elements/mod_assign_output_user_submission'
);

If you are using less as bootstrapbase does then you will use:

/** In your primary less file **/
@import "moodle/elements/core_output_button";
@import "moodle/elements/core_output_navigation_bar";
@import "moodle/elements/mod_assign_output_user_submission";

General CSS

This is CSS for the theme that doesn't relate to a specific element. Hopefully as we convert more and more of Moodles interfaces to use elements we should see less general CSS.

In any case nothing has changed in regards to the location of general CSS.

CSS 
If your theme is using standard CSS then it must be located within the style directory of your theme and should be in logically separated files.
Less 
If your theme is using less then it should be located in a less/moodle directory within your theme.

We recommend that you separate styles into files based upon the Moodle component being styled. Again as all CSS files get combined into a single file on production sites it doesn't matter how many files you end up with.

This would lead to files named like:

  • style/core.css
  • style/course.css
  • style/mod_forum.css

Where to put CSS in plugins

It is possible in Moodle to add CSS stylesheets to all plugins (including core plugins).
Moodle always looks for a styles.css file within your plugins directory, and it also looks for a styles_themename.css where themename is the name of the currect theme being used.

For instance the following could be added to the forum module:

  • mod/forum/styles.css: Used all the time.
  • mod/forum/styles_base.css : Used when the user is using the base theme.
  • mod/forum/styles_bootstrapbase.css : Used when the user is using the bootstrapbase theme.
  • mod/forum/styles_clean.css : Used when the user is using the clean theme.

CSS in core plugins

For core plugins only styles.css is permitted and it should only ever contain styles essential to the presentation of the plugin.
At present styles essential for the display of the plugin are allowed here, however when the plugin interfaces have been converted to use elements this file should be nearly empty and contain only the following:

  • Images, icons and text used for visual presentation such as icons to notify state (expanded/collapsed, warnings...)
  • Accessiblity orientated styles required to make the interface accessible.

Under no circumstances should it contain any visual stylings such as border, rounded corners, and background colours not associated with state.

HTML

The following sections detail rules in place for writing HTML.

Before we start a couple of general recommendations:

Use hyphens rather than underscores when concatenating strings in attributes 
for example .course-new-activity-chooser instead of .course_new_activity_chooser. By all doing this we will have a more consistent appearance and as We use underscores as part of frankenstyle which is prominent in CSS helps us to reduce the chance of naming conflicts.
Think semantics - but don't get caught up in it 
Semantic CSS is beautiful CSS lending itself ultimately to the redesign that is going to happen. But enforcing a semantic design can be an arduous journey. Instead we encourage semantic CSS but are happy to see non-semantic CSS when it is for the greater good.

Using the ID attribute

The situation of having a single JavaScript driven element was considered but even then it is better to style only classes and other attributes as we can't be sure what the future holds.

There are three rules you should follow when creating an ID:

  1. They should only be given to elements and HTML nodes that need to be interacted with by JavaScript.
  2. It should always start with m- (see note below)
  3. The actual ID should be descriptive of the node and contents as a whole unit. It must fully and concisely describe its purpose.

Keep in mind that in an ideal world ID's should not be used for styling.

Good

<div id="m-dock"> ... </div>
 
<div id="m-course-new-activity-chooser"> ... </div>

Bad

<!--
It doesn't have the m- prefix
-->
<div id="dock"> ... </div>
 
<!--
This may appear OK at first but when you think about it selecting an activity within Moodle
is something that is done in multiple places and the this widget may not be applicable to
every place, as there may be other activity selector JS one day introduced having a
more complete ID is important
-->
<div id="m-activity-selector"> ... </div>

Note: The requirement to use m- prefix is entirely new, this has been created as this instantly reduces the chances of conflicting with present and future frontend frameworks that may be adopted by core or third party themes. Aiding our goal of being frontend framework agnostic.
It will also help reduce the chance of conflicts in any third party code that may be used either by us in core, or by outside developers in their own code. Moodle ID's will also be much more easily recognisable which we expect will be a benefit as JavaScript use in Moodle continues to increase.

Naming CSS classes

This is where a bit of organisation goes a long way and we want to be reasonably thorough in a consistent naming scheme and implementation.

The very first thing to mention is that all classes in Moodle must now be prefixed by *m-*. This instantly identifes a class as belonging to Moodle, it aids us in avoiding conflicts with any non-Moodle css that may be loaded, and it is part of our framework agnostic plan.

All CSS classes must start with *m-*

There are several prefix that are used within Moodle output now that should not be used as prefixs for classes unless they are serving the desired purpose.

Reserved prefixes are:

m-element 
Prefix used on element classes, where m-element preceeds the name of the element, e.g. m-element-button, m-element-search
m-state 
Prefix used for CSS classes representing the state of an item, e.g. m-state-disabled, m-state-active, m-state-expanded.
m-grid 
Prefix used for creating HTML grids.

Elements

Semantic design where it gives the best result, smart design where there is a better solution.

If you're not already familiar with the idea of semantic CSS then Chris Coylers artical What makes for a semantic class name is a great starting point.

We've not commit to adopting any one strict approach for writing CSS. We have adopted atomic design for our HTML elements, a design style which fits nicely with the likes of OOCSS, however be very clear that we are not adopting the OOCSS approach in its entirety. The high level abstraction of the visual aspects of elements into highly reusable agnostic classes does not always lend itself well to the Moodle world in which the we wish to be frontend framework agnostic and support largescale completely customisable themes.
The highly reusable classes that appeal to one designer, or to one frontend framework may not apply to another.
This decision is also impacted by our current default theme bootstrapbase that does not use well abstracted OOCSS in its design.

Atomic design is going to see us end up with many parent child relationships in our HTML that we must carry through to our CSS. Through atomic design we start with smalled atoms and molecules that get built into larger organisms and placed within layouts.

Every element should have a single class that identifies it as element X.

For instance the button atom will have the class *m-element-button*

<button class="m-element-button">I am a button</button>

This is simply the prefix "m-element" plus the element name.

The purpose of this class is to allow the element to be styled with ease. The class name should be applied to the root node for the element, it should never be applied any lower.

By having one class for the element, and always and only applied to the root node it makes it very easy in HTML to see where one element belongs to another.

The following is an example of the search molecule, which in turn contains two button atoms.

<form class="m-element-confirmation">
    <h3>Are you sure?</h3>
    <p>Are you sure you want to perform this action?</p>
    <input type="submit" name="cancel" class="m-element-button" value="Cancel" />
    <input type="submit" name="continue" class="m-element-button" value="Continue" />
</form>

We've chosen to partially adopt BEM as a CSS standard when writing CSS for elements.

BEM stands for Block-Element-Modifer. It is a notation format for use with CSS to better describe the relationship between a component and it's constituent parts.

The notation is simple and consists of just 3 variants:

.block {}
.block__element {}
.block--modifier {}
  • .block class represents our component e.g. .site-nav
  • .block__element class represents part of our overall component e.g. .site-nav__link (NB: we use 2 underscores between the block prefix and the element suffix to distinguish it from being part of a hyphenated class name)
  • .block--modifier class represents a variation in the type or state of our component e.g. .site-nav--vertical would allow us to vary the display of our navigation links. (NB: we use 2 dashes between the block prefix and the modifier suffix)

Let's apply the BEM notation to something more tangible and define a simple abstraction for a bottle component:

.bottle {}
.bottle__cap {} 
.bottle__label {} 
.bottle__branding {} // a logo  
.bottle--screwcap {} // a variation on a standard bottle
.bottle--corked {}
.bottle--empty {} // our bottle is in an empty state
.bottle--recycled {} // our bottle is in a recycled state

and this might be how we would markup our bottle component:

<!-- lets pick a general purpose container -->
<div class="bottle">
  <header class="bottle__cap"></header>
  <p class="bottle__label">Natural Spring Water</p>
  <img class="bottle__branding" />
</div>
 
<!-- what if we wanted wine instead? -->
<div class="bottle bottle--corked">
  <header class="bottle__cap"></header>
  <p class="bottle__label">Cheap plonk!</p>
  <img class="bottle__branding" /> 
</div>
 
<!-- what if we're all out? -->
<div class="bottle bottle--corked bottle--empty">
  <header class="bottle__cap"></header>
  <p class="bottle__label">The good stuff!</p>
  <img class="bottle__branding" /> 
</div>

So you can see, using BEM notation, we can be very expressive in the definition of the elements that make up our pages. And as an added bonus, we are building up a repository of components that we can reuse elsewhere in our pages.

Lets say for the purposes of this example that our bottle component includes a list of ingredients. We don't need anything fancy, just a regular old list with all the default formatting removed:

.list-bare {
// remove default bullets, margin and padding!
}

This kind of list sounds like something we could use elsewhere, so instead of defining it as part of our bottle component, lets make it a standalone component:

<div class="bottle bottle--corked">
  <header class="bottle__cap"></header>
  <p class="bottle__label">The good stuff!</p>
  <img class="bottle__branding" />
  <h3>Ingrediants</h3>
  <ul class="list-bare">
    <li>Lots of Grapes</li>
    <li>A dash of Yeast</li>
    <li>A whole lot of love</li>
  </ul>
</div>

Now, when we need to use a simple unstyled list, we have a component to do just that. The longer you work with BEM notation, the more aware you become of the potential reuse of your components and size of your stylesheets become smaller and more manageable.

You'll notice in the example a little further up that there is no way to style the continue button differently from the cancel button without relying on its name attribute, something we certainly don't want to do. The BEM standard comes into play here in naming the classes, the following shows how you would apply BEM to show the purpose of each button in a way that can be easily interpreted and styled in CSS. We will add additional classes to our 2 input elements to set them apart from standard buttons. You can consider m-element-button as a base class upon which we can build many variants for different purposes.

<form class="m-element-confirmation">
    <h3 class="m-element-confirmation__title">Are you sure?</h3>
    <p class="m-element-confirmation__text">Are you sure you want to perform this action?</p>
    <input type="submit" name="cancel" class="m-element-button '''m-element-button--cancel'''" value="Cancel" />
    <input type="submit" name="continue" class="m-element-button '''m-element-button--continue'''" value="Continue" />
</form>

By including the class name for m-element-button--continue one of our form buttons assumes the additional styles we defined for our form's primary action.

Now to explain the idea of intent. An intent is when a single element takes on a different apprearance to convey a specific meaning. A little bit like a state, but with more meaning. An easy example is notifications where you will no doubt be familiar with the idea of having a error notifications, warning notifications, success notification etc. Each of error, warning, and success is a intent.

The following shows how this would work:

<!-- A standard notification -->
<div class="m-element-notification">You have not changed your password in the past 60 days</div>
 
<!-- A warning -->
<div class="m-element-notification m-element-notification--warning">You have not changed your password in the past 60 days</div>
</div>

Grids

We need to adopt a single grid framework.

Personally I don't think we should use pure bootstrap or pure YUI, instead we should choose one of these and build it use our class name structures.

All grid classes should start with m-grid.

CSS

Before we begin lets lay down some ground level goals, things that will help us achieve clear and understandable CSS in the future.

  • Formatting, ordering, alignment, and spacing are all important in having CSS that can be easily read by others.
  • Don't optimise your CSS, be verbose and stick to the styles in this guide. We have tools that will handle optimisation for you.
  • Never use *!important* in your CSS unless it is absolutely impossible to avoid doing so.

Terminology

Its important that when we are speaking about CSS we are speaking the same language. Lets look at the following example:

.m-page .m-selector-1,
.m-page .m-selector-2,
.m-page .m-selector-3 {
    background-color: #FFF;
    color: #000;
}
 
.m-selector-1 {
    margin: 1em;
}
Ruleset 
Each of the two rules is a single *ruleset*.
Rule
_.m-page .m-selector-1_ is a rule.
Selector 
_.m-selector-1_ is a *selector*, it is a single part of a rule.
Declaration 
_background-color: #FFF_ is a declaration.

Essential formatting

The following are the guies for writing CSS.

  • Always use spaces - never use tabs. All editors can be configured to do it.
  • Indent by 4 spaces - never more, never less.
  • A single empty line between rulesets to maintain consistent spacing of rules.
  • One rule per line.
  • A single space between each selector in a rule.
  • No space between a rule and the comma in a ruleset.
  • A single space between a selector and the opening brace of a ruleset.
  • One level of indentation before each declaration.
  • A single space the colon in a declaration.
  • A single space after each item in a declaration containing multiple items.

The following shows how the above rules apply:

.m-selector-1,
.m-selector-2 .m-selector-3 {
    background: #ffa800 url("background.png") no-repeat right top;
    border: 1px solid #000;
    display: inline-block;
    font-family: arial, sans-serif;
    margin: 5px 10px;
    padding: 0;
}
 
.m-selector-4 {
    border-radius: 3px;
}

General rules

Declaration ordering is the very first thing to discuss. There is no strict rule on how to order your declarations, however there is a recommendation. We recommend that you order declarations within a ruleset alphabetically. If we all start doing this then when you read CSS written by someone else you will soon learn to very quickly find your way around the declarations within a rule.

/** Recommended **/
.m-selector {
    background-color: #fff;
    border: 1px solid #000;
    color: #000;
    display: block;
    margin: 5px;
    padding: 5px;
}

Do not use a measurement unit when writing a 0 value.

/** Good **/
.m-selector {
    margin: 5px 0;
}
 
/** Bad **/
.m-selector {
    margin: 5px 0px;
}

Use lowercase and shorthand values for colours.

/** Good **/
.m-selector {
    background-color: #fff;
}
 
/** Bad **/
.m-selector {
    background-color: #ffffff;
}
.m-selector {
    background-color: #FFFFFF;
}
.m-selector {
    background-color: white;
}

Always quote variable values.

/** Good **/
.m-selector[attr="variable"] {
    background-image: url("image.png");
}
 
/** Bad **/
.m-selector[attr=variable] {
    background-image: url(image.png);
}

Commenting

Always comment your CSS and Less.

/**
 * Styles for the button atom.
 */
.m-element-button {
}

Rules and exceptions for less

Need to write some guff on Less.

See also