Skip to content

Instantly share code, notes, and snippets.

@fat
Last active December 10, 2024 21:04
Show Gist options
  • Save fat/a47b882eb5f84293c4ed to your computer and use it in GitHub Desktop.
Save fat/a47b882eb5f84293c4ed to your computer and use it in GitHub Desktop.

LESS Coding Guidelines

Medium uses a strict subset of LESS for style generation. This subset includes variables and mixins, but nothing else (no nesting, etc.).

Medium's naming conventions are adapted from the work being done in the SUIT CSS framework. Which is to say, it relies on structured class names and meaningful hyphens (i.e., not using hyphens merely to separate words). This is to help work around the current limits of applying CSS to the DOM (i.e., the lack of style encapsulation) and to better communicate the relationships between classes.

Table of contents

JavaScript

syntax: js-<targetName>

JavaScript-specific classes reduce the risk that changing the structure or theme of components will inadvertently affect any required JavaScript behaviour and complex functionality. It is not neccesarry to use them in every case, just think of them as a tool in your utility belt. If you are creating a class, which you dont intend to use for styling, but instead only as a selector in JavaScript, you should probably be adding the js- prefix. In practice this looks like this:

<a href="/login" class="btn btn-primary js-login"></a>

Again, JavaScript-specific classes should not, under any circumstances, be styled.

Utilities

Medium's utility classes are low-level structural and positional traits. Utilities can be applied directly to any element; multiple utilities can be used together; and utilities can be used alongside component classes.

Utilities exist because certain CSS properties and patterns are used frequently. For example: floats, containing floats, vertical alignment, text truncation. Relying on utilities can help to reduce repetition and provide consistent implementations. They also act as a philosophical alternative to functional (i.e. non-polyfill) mixins.

<div class="u-clearfix">
  <p class="u-textTruncate">{$text}</p>
  <img class="u-pullLeft" src="{$src}" alt="">
  <img class="u-pullLeft" src="{$src}" alt="">
  <img class="u-pullLeft" src="{$src}" alt="">
</div>

u-utilityName

Syntax: u-<utilityName>

Utilities must use a camel case name, prefixed with a u namespace. What follows is an example of how various utilities can be used to create a simple structure within a component.

<div class="u-clearfix">
  <a class="u-pullLeft" href="{$url}">
    <img class="u-block" src="{$src}" alt="">
  </a>
  <p class="u-sizeFill u-textBreak"></p>
</div>

Components

Syntax: <componentName>[--modifierName|-descendantName]

Component driven development offers several benefits when reading and writing HTML and CSS:

  • It helps to distinguish between the classes for the root of the component, descendant elements, and modifications.
  • It keeps the specificity of selectors low.
  • It helps to decouple presentation semantics from document semantics.

You can think of components as custom elements that enclose specific semantics, styling, and behaviour.

ComponentName

The component's name must be written in camel case.

.myComponent { /* … */ }
<article class="myComponent"></article>

componentName--modifierName

A component modifier is a class that modifies the presentation of the base component in some form. Modifier names must be written in camel case and be separated from the component name by two hyphens. The class should be included in the HTML in addition to the base component class.

/* Core button */
.btn { /* … */ }
/* Default button style */
.btn--default { /* … */ }
<button class="btn btn--primary"></button>

componentName-descendantName

A component descendant is a class that is attached to a descendant node of a component. It's responsible for applying presentation directly to the descendant on behalf of a particular component. Descendant names must be written in camel case.

<article class="tweet">
  <header class="tweet-header">
    <img class="tweet-avatar" src="{$src}" alt="{$alt}"></header>
  <div class="tweet-body"></div>
</article>

componentName.is-stateOfComponent

Use is-stateName for state-based modifications of components. The state name must be Camel case. Never style these classes directly; they should always be used as an adjoining class.

JS can add/remove these classes. This means that the same state names can be used in multiple contexts, but every component must define its own styles for the state (as they are scoped to the component).

.tweet { /* … */ }
.tweet.is-expanded { /* … */ }
<article class="tweet is-expanded"></article>

Variables

Syntax: <property>-<value>[--componentName]

Variable names in our CSS are also strictly structured. This syntax provides strong associations between property, use, and component.

The following variable defintion is a color property, with the value grayLight, for use with the highlightMenu component.

@color-grayLight--highlightMenu: rgb(51, 51, 50);

Colors

When implementing feature styles, you should only be using color variables provided by colors.less.

When adding a color variable to colors.less, using RGB and RGBA color units are preferred over hex, named, HSL, or HSLA values.

Right:

rgb(50, 50, 50);
rgba(50, 50, 50, 0.2);

Wrong:

#FFF;
#FFFFFF;
white;
hsl(120, 100%, 50%);
hsla(120, 100%, 50%, 1);

z-index scale

Please use the z-index scale defined in z-index.less.

@zIndex-1 - @zIndex-9 are provided. Nothing should be higher then @zIndex-9.

Font Weight

With the additional support of web fonts font-weight plays a more important role than it once did. Different font weights will render typefaces specifically created for that weight, unlike the old days where bold could be just an algorithm to fatten a typeface. Obvious uses the numerical value of font-weight to enable the best representation of a typeface. The following table is a guide:

Raw font weights should be avoided. Instead, use the appropriate font mixin: .font-sansI7, .font-sansN7, etc.

The suffix defines the weight and style:

N = normal
I = italic
4 = normal font-weight
7 = bold font-weight

Refer to type.less for type size, letter-spacing, and line height. Raw sizes, spaces, and line heights should be avoided outside of type.less.

ex:

@fontSize-micro
@fontSize-smallest
@fontSize-smaller
@fontSize-small
@fontSize-base
@fontSize-large
@fontSize-larger
@fontSize-largest
@fontSize-jumbo

See Mozilla Developer Network — font-weight for further reading.

Line Height

Type.less also provides a line height scale. This should be used for blocks of text.

ex:

@lineHeight-tightest
@lineHeight-tighter
@lineHeight-tight
@lineHeight-baseSans
@lineHeight-base
@lineHeight-loose
@lineHeight-looser

Alternatively, when using line height to vertically center a single line of text, be sure to set the line height to the height of the container - 1.

.btn {
  height: 50px;
  line-height: 49px;
}

Letter spacing

Letter spacing should also be controlled with the following var scale.

@letterSpacing-tightest
@letterSpacing-tighter
@letterSpacing-tight
@letterSpacing-normal
@letterSpacing-loose
@letterSpacing-looser

Polyfills

mixin syntax: m-<propertyName>

At Medium we only use mixins to generate polyfills for browser prefixed properties.

An example of a border radius mixin:

.m-borderRadius(@radius) {
  -webkit-border-radius: @radius;
     -moz-border-radius: @radius;
          border-radius: @radius;
}

Formatting

The following are some high level page formatting style rules.

Spacing

CSS rules should be comma seperated but live on new lines:

Right:

.content,
.content-edit {
  …
}

Wrong:

.content, .content-edit {
  …
}

CSS blocks should be seperated by a single new line. not two. not 0.

Right:

.content {
  …
}
.content-edit {
  …
}

Wrong:

.content {
  …
}

.content-edit {
  …
}

Quotes

Quotes are optional in CSS and LESS. We use double quotes as it is visually clearer that the string is not a selector or a style property.

Right:

background-image: url("/img/you.jpg");
font-family: "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial;

Wrong:

background-image: url(/img/you.jpg);
font-family: Helvetica Neue Light, Helvetica Neue, Helvetica, Arial;

Performance

Specificity

Although in the name (cascading style sheets) cascading can introduce unnecessary performance overhead for applying styles. Take the following example:

ul.user-list li span a:hover { color: red; }

Styles are resolved during the renderer's layout pass. The selectors are resolved right to left, exiting when it has been detected the selector does not match. Therefore, in this example every a tag has to be inspected to see if it resides inside a span and a list. As you can imagine this requires a lot of DOM walking and and for large documents can cause a significant increase in the layout time. For further reading checkout: https://developers.google.com/speed/docs/best-practices/rendering#UseEfficientCSSSelectors

If we know we want to give all a elements inside the .user-list red on hover we can simplify this style to:

.user-list > a:hover {
  color: red;
}

If we want to only style specific a elements inside .user-list we can give them a specific class:

.user-list > .link-primary:hover {
  color: red;
}
@evanknight
Copy link

This is awesome. I'm curious, what does your directory structure look like? Do you have a separate mixins folder like in Bootstrap?

@nchase
Copy link

nchase commented Aug 29, 2014

At Medium we only use mixins to generate polyfills for browser prefixed properties.

Any reason you aren't using autoprefixer to do this for you?

@chriscamargo
Copy link

Why the preference for RGB(A) over HEX? Simply a matter of consistency across colors that utilize alphas and those that don't?

@chendrix
Copy link

The class should be included in the HTML in addition to the base component class.

What is your or Nicolas's reasoning or justification behind this?

I know you also stated in your 3rd style guide that:

Mixins

Make sure to take into consideration the output of using LESS' powerful mixins. They are best used for grouping browser-specific code or as powerful ways to contain functionality. They are not a good way to add additional styles to an element as you will be sending duplicate styles across the wire. [emphasis mine]

And I'm wondering what the justification for that is as well.

Is there a significant performance impact that you see when you mixin the base component into modified components? Because to me it seems like you are sacrificing succinctness in your HTML to save a few lines of a generated CSS file you will never read anyways (and probably has little performance impact when sent over the wire.)

What happens if someone adds in .btn--primary but forgets to also include .btn in their HTML?

@srph
Copy link

srph commented Sep 20, 2014

Does this adhere to this guideline? Although I am using SaSS, I want to try out this guideline.

.u-spanHr {
    @include u-span();

    /**
     * Provides equal spacing to both hr sides
     */
    &.u-spanHr--1eq {
        @include u-spanHr( floor($span-1 / 2), floor($span-1 / 2) );
    }
    &.u-spanHr--2eq {
        @include u-spanHr( floor($span-2 / 2), floor($span-2 / 2) );
    }
    &.u-spanHr--3eq {
        @include u-spanHr( floor($span-3 / 2), floor($span-3 / 2) );
    }
    &.u-spanHr--4eq {
        @include u-spanHr( floor($span-4 / 2), floor($span-4 / 2) );
    }
    &.u-spanHr--5eq {
        @include u-spanHr( floor($span-5 / 2), floor($span-5 / 2) );
    }

    /**
     * Provides spacing to an element's `BOTTOM` side
     */
    &.u-spanHr--1 {
        @include u-spanHr( $span-1 );
    }
    &.u-spanHr--2 {
        @include u-spanHr( $span-2 );
    }
    &.u-spanHr--3 {
        @include u-spanHr( $span-3 );
    }
    &.u-spanHr--4 {
        @include u-spanHr( $span-4 );
    }
    &.u-spanHr--5 {
        @include u-spanHr( $span-5 );
    }
}

@waltfy
Copy link

waltfy commented Oct 9, 2014

@chendrix 👍

The class should be included in the HTML in addition to the base component class.

I am also interested on the justification for this, because it would be nice to just drop:

html <a class='btn--primary'>Button</a>

Thus using the .btn class as a mixin?

@aaronmw
Copy link

aaronmw commented Oct 17, 2014

@waltfy: For a case against the single-class pattern, Nicolas Gallagher writes about it here: http://nicolasgallagher.com/about-html-semantics-front-end-architecture/#the-single-class-pattern

@adamwathan
Copy link

@chendrix and @waltfy

The thing with CSS and HTML is that a modifier class like btn--primary doesn't really work as a modifier of btn unless both classes are specified in the markup, that's just the way CSS is applied by the browser.

Think about something like this:

<div class="sidebar">
  <button class="btn--primary">Main button</button>
  <button class="btn--secondary">Second button</button>
</div>

Say all your buttons have rounded corners, but you want any button in the sidebar to have square corners.

Both of your buttons have no common class to target. They aren't actually btn components unless the class attribute says they are; the mixin doesn't make a difference here.

If you used a multi-class pattern, you could just use .sidebar .btn and set the corners to square.

The other option is to use &:extend but then you end up generating a ton of CSS bloat for no real reason other than to reduce the number of classes you are using in your HTML. You end up fighting how the browser works for no appreciable gain as far as I can tell. Why is it bad to have more classes in the HTML? What problem are you solving by making your class attributes shorter?

I used to be firmly on the other side, in favor of deep nesting and almost class-less HTML, to keep the markup "pure". What I found in doing that was that my styles were completely coupled to my markup, and nothing was reusable at all anywhere. You end up with completely unmaintainable Less/Sass, fighting specificity wars everywhere, and a massive compiled CSS file, all in the name "clean markup", for which I haven't actually found a justification for.

@zsitro
Copy link

zsitro commented Nov 3, 2014

@fat or anyone. Please help me understand the preference for rgb/rgba color definitions besides the alpha thing.
It's harder to copy from PSD or from wherever I want, takes longer to input, etc.

alpha channel supprt can't be a factor for me as I can do color: rgba($colorRed, .8) or similar.

Moreover you have to do the same if you're using rgb() everywhere cuz you define a color as rgb() but when you need the color for a semi-transparent background you'll need to re-define another color with alpha channel or use the same approach as above (color: rgba($colorRed, .8)).

Moreover I can almost memorize or at least easily recognize hex values rather than rgb ones.

@johanhelsing
Copy link

I am a bit confused about the last example.

.user-list > .link-primary:hover {
  color: red;
}

I assume .link-primary is a typo and it should instead be .link--primary. Otherwise, wouldn't .link-primary be a component descendant named primary of the ancestor link?

@waltfy
Copy link

waltfy commented Mar 18, 2015

@adamwathan – first of all sorry for the ridiculously long time gap – I see your point. It makes sense to be able to do something like .someComponent .btn and have all buttons behave the same within .someComponent, rather than having to manipulate modifier classes.

It just allows for better levels of control over the styling of elements. Correct me if I am wrong.

@samuelbeek
Copy link

Why is

<a href="/login" class="btn btn-primary js-login"></a>

not

 <a href="/login" class="btn btn--primary js-login"></a>

?

@thebigtine
Copy link

@fat its been asked twice, but never answered. Why rgb/rgba over hex?

@proYang
Copy link

proYang commented Aug 8, 2016

Seeking translation

@MAYDAY1993
Copy link

hello,I've just read this article and I'm wondering why line-height=height-1 ,because I have tried line-height=height and it worked well ,so can you tell me the reason or give me some links to read about? Thank you!!!!

@MAYDAY1993
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment