HTML/CSS/Sass


In web development, HTML forms the basic skeletal structure, and CSS decides how the markup language will be presented. Both are closely related to service performance and accessibility. In other words, both HTML and CSS must be written well in order for all browsers to fully express the contents without significant loss. This guide serves to help developers write consistent codes to facilitate cooperation and to minimize maintenance and expansion cost.

Table of Contents

HTML

Basic Style

Use two empty spaces to indent.

<!-- Bad -->
<table>
    <tr>
        <th>Name</th>
        <th>Favorite Color</th>
    </tr>
 ...
</table>

<!-- Good -->
<table>
  <tr>
    <th>Name</th>
    <th>Favorite Color</th>
  </tr>
 ...
</table>

Use double quotation marks ("") for attribute values.

<!-- Bad -->
<a href='/' class='home'>Home</a>

<!-- Good -->
<a href="/" class="home">Home</a>

Only use lowercase for tag names, attribute names, and attribute values.

<!-- Bad -->
<A HREF="/">Home</A>

<!-- Good -->
<a href="/">Home</a>

Clearly specify the HTML doctype.

For the sake of consistent rendering within browsers, always specify HTML5 DOCTYPE at the top of the code. XHTML is not recommended because it lacks browser support as well as tool support, and it no longer fits the standards of modern browsers.

<!DOCTYPE html>
<html>
  <head>
  </head>
</html>

Define the document’s language by giving it a lang attribute to the outer most <html> tag. Screen Readers use the lang attributes to detect the documents’ languages and transform text into audio or provide appropriate pronunciations.

Note HTML5 Element further explains the lang attribute. List of language codes

<html lang="ko">
  <!-- ... -->
</html>

Internet Explorer uses meta tags to make sure that the page is rendered correctly given a specific version. To prevent IE from rendering differently, use the latest Edge mode.

Note To learn more about IE Compatibility, refer to this stack overflow article.

<meta http-equiv="X-UA-Compatible" content="IE=Edge">

If a metatag charset is used to specify the character encoding scheme, browsers and development tools detect the document’s encoding faster and more accurately. Character encoding is based on Unicode, and it is recommended to use the most widely used UTF-8 because of its excellent backwards compatibility.

Note To learn more about encoding schemes used for HTML and CSS, refer to W3C Tutorial.

<html lang="ko">
  <meta charset="UTF-8">
</head>

Do not explicitly specify the values of Boolean attributes.

Do not explicitly specify the values of Boolean attributes like selected, disabled, and checked.

<!-- Bad -->
<input type="text" disabled=true>

<!-- Good -->
<input type="text" disabled>

<!-- Good -->
<input type="checkbox" value="1" checked>

<!-- Bad -->
<input type="checkbox" value="1" checked=true>

<!-- Good -->
<select>
  <option value="1" selected>1</option>
</select>

<!-- Bad -->
<select>
  <option value="1" selected=true>1</option>
</select>

Avoid unnecessary tags.

If at all possible, avoid using unnecessary tags. The size of the HTML document directly correlates with the amount of data transferred over the network. The depth of the HTML document affects the weight of the render tree browsers use to render, so lighter files render faster. [FE Guide] Performance Guide explains the rendering performance in greater detail.

<!-- Bad -->
<span class="tui">
  <img src="...">
</span>

<!-- Good -->
<img class="tui" src="...">

Use HTML tags with purpose.

Use HTML tags with purpose. For example, only use <h> tag to represent a header, use <p> tag to represent a paragraph, and use <a> tag to represent a hyperlink. Also, using new semantic tags introduced in HTML5 like <header>, <nav>, and <article> benefits accessibility, reusability, and Search Engine Optimization (SEO).

<!-- Bad -->
<p><strong>All recommendations</strong></p>

<!-- Good -->
<h3>All recommendations</h3>

<!-- Bad -->
Some text
<br />
<br />
Some more text

<!-- Good -->
<p>Some text</p>
<p>Some more text</p>

<!-- Bad -->
<div onclick="bad();">go</div>

<!-- Good -->
<a href="good/">go</a>

Do not use entity references.

Do not use entity references. If the document follows the same encoding scheme appropriately, every character appears as typed, so entity references like &mdash;,&rdquo;, and &#x263a; are unnecessary. However, reserved characters in HTML like < or & require entity references.

<!-- Bad -->
The currency symbol for the Euro is `“&eur;”`.

<!-- Good -->
The currency symbol for the Euro is “€”.

When using external CSS/JS files, do not use the type attribute.

According to the specifications of HTML5, the file type attributes are already set as text/css and text/javascript when using external files, so it is unnecessary to specify them with attribute values.

Note HTML5 Specifications Style HTML5 Specifications Script HTML5 Specifications

<!-- Bad -->
<link rel="stylesheet" href="//uicdn.toast.com/tui.chart/latest/tui-chart.min.css" type="text/css">

<!-- Good --> 
<link rel="stylesheet" href="//uicdn.toast.com/tui.chart/latest/tui-chart.min.css">

<!-- Bad -->
<script src="//uicdn.toast.com/tui.chart/latest/tui-chart.min.js" type="text/javascript"></script>

<!-- Good -->
<script src="//uicdn.toast.com/tui.chart/latest/tui-chart.min.js"></script>

CSS and JavaScript are linked at top and bottom, respectively.

When JavaScript is included in the <head>, the page rendering is postponed until all of the JavaScript files have finished downloading, parsing, and compiling, so it is better to include it at the bottom of the body. (Related file: [FE Guide] Include the script file at the bottom of the document.

<!-- Bad -->
<!DOCTYPE html>
<html>
    <head>
        <title>HTML Page</title>
        <link rel="stylesheet" href="//uicdn.toast.com/tui.chart/latest/tui-chart.min.css" type="text/css">
        <script src="//uicdn.toast.com/tui.chart/latest/tui-chart.min.js"></script>
    </head>
    ...
</html>

<!-- Good -->
<!DOCTYPE html>
<html>
    <head>
        <title>HTML Page</title>
        <link rel="stylesheet" href="//uicdn.toast.com/tui.chart/latest/tui-chart.min.css" type="text/css">
    </head>
    <body>
        ...
        <!-- Included at the end of the body tag-->
        <script src="//uicdn.toast.com/tui.chart/latest/tui-chart.min.js"></script>
    </body>
</html>

CSS

Basic Style

Use two empty spaces to indent. Instead of using camelCase and snake_case, kebab-case is used to name classes and ids. For the sake of readability, use a single space before the opening declarative curly bracket ({) and place the ending curly bracket (}) on a separate line.

/* Bad */
.firstSelector {}
#buttonId {}
#button-id{
...}

/* Good */
.first-selector {}
#button-id {
  ...
}

At the point of declaration, add a single space after a colon :.

/* Bad */
.selector {
  padding:15px; 
  margin : 15px;
}


/* Good */
.selector {
  padding: 15px;
  margin: 15px;
}

To describe a single attribute, use one line. Contrarily, use one line per attribute to describe multi attributed properties.

/* Bad */
.selector {
  padding:15px;
}
.selector {padding: 15px;margin: 15px;}

/* Good */
.selector {padding:15px;}
.selector {
  padding: 15px;
  margin: 15px;
}

When referencing multiple selectors, list them out in one line.

/* Bad */
.selector, .selector-secondary, .selector[type=text] {
  ...
}

/* Good */
.selector,
.selector-secondary,
.selector[type="text"] {
  ...
}

At the end of every style statement, use a semicolon. Although the semicolon is optional for the last statement, leaving it out makes the file more susceptible to errors.

/* Bad */
.selector {
  color: blue;
  margin: 20px;
  padding:15px
}


/* Good */
.selector {
  color: blue;
  margin: 20px;
  padding: 15px;
}

Use classes instead of ids to set styles.

Always use classes to set styles. Ids have zero reusability, but have infinitely high specificity. Such characteristics could lead to unpredictable behaviors. Only use ids in special cases like link management and for statements.

Note To learn more about specificity, refer to this article on CSS Wizardry.

Do not use classes designed to set styles with JavaScript hooks.

When using JavaScript to register an event handler to the DOM, do not use classes designed to set styles. CSS style statements and JavaScript action control have different responsibilities, so managing them separately makes maintenance easier. In this case, attaching a js- prefix in front of classes used for JavaScript is recommended.

<button class="btn js-submit">submit</button>

Do not use unnecessary units of measurement after the numerical value 0.

/* Bad */
margin: 1px 0px 2px 0px;
padding: 0px;

/* Good */
margin: 1px 0 2px 0;
padding: 0;

flex: 0px; // For flex-basis component, unit of measurement is required.

If there is no border, use 0 instead of none.

/* Bad */
.no-border {border: none;}

/* Good */
.no-border {border: 0;}

Colors

When using hexadecimals to represent colors, use three characters, as shown in the example below, if possible.

/* Bad */
color: #eebbcc;

/* Good */
color: #ebc;

Use class selectors instead of tag selectors.

In order to maintain high rendering performance, if at all possible, use class selectors instead of tag selectors.

/* Bad */
span { ... }

While it is possible to use tag selectors with attribute selectors (eg>[class^="..."]), using the two together causes performance issues, so should be avoided. To use the type selector, use double quotation marks. (eg>input[type="text"])

/* Bad */
span[class^="main"] { ... }

/* Good */
.main { ... }

Note Fore more examples dealing with type selectors, refer to this document.

Limit the selector length to maximum of three, and try to keep it as short as possible. Parent selector should be included only if it must be included.

/* Bad */
.page-container #stream .stream-item .tweet .tweet-header .username { ... }

/* Good */
.tweet-header .username { ... }

If at all possible, use abbreviations.

For properties like padding, margin, font, background, border, and border-radius, use abbreviations if possible.

/* Bad */
font-family: 'Open Sans', 'Noto Serif ', sans-serif;
font-size: 100%;
line-height: 1.6;
padding-bottom: 2px;
padding-left: 1px;
padding-right: 1px;
padding-top: 0;

/* Good */
font: 100%/1.6 'Open Sans', 'Noto Serif ', sans-serif;
padding: 0 1px 2px;

Abbreviating should not rampage overboard.

/* Bad */
.element {
  margin: 0 0 10px 0;
  background: #c83636;
  background: url("back.jpg");
}

/* Good */
.element {
  margin-bottom: 10px;
  background-color: #c83636;
  background-image: url("back.jpg");
}

Compared to <link>, @import takes too long; therefore, it should not be used. If an @import is used within the <link>, the browser cannot process the CSS while the @imported materials are downloading, so the loading time increases dramatically. Also, if an error occurs during the download sequence, such errors are incredibly hard to detect.

<!-- Bad -->
<style>
  @import url("more.css");
</style>

<!-- Good -->
<link rel="stylesheet" href="core.css">

Note To see more detailed experiment results regarding import, refer to an article by Steve Souders.

Sass

Use .scss syntax.

Instead of using .sass syntax based on indentation, use scss syntax which is built to be compatible with every syntax and functionality in CSS.

/* .sass syntax */
.black-div
  background: black
  border: 1px solid #ccc
  span 
    padding: 10px;

/* .scss syntax */
.black-div {
  background: black;
  border: 1px solid #ccc;
  span {
    padding: 10px;
  }
}

Order of declaration

The order of declaration is as follows: attribute, @include, and nested selectors. After @include, always insert a new line.

.black-div {
  background: black;
  border: 1px solid #ccc;
  @include transition(background 0.5s ease)

  .icon {
    padding: 10px;
  }
}

Use Kebab Case for variable names.

Instead of camelCase or snakecase, use kebab-case that uses a dash - to name variables. If the file is local, it is possible to use it with an underscore (``).

/* Bad */
$mainColor: blue;
$main_color: blue;

/* Good */
$main-color: blue;

Mixin

Mixin should be used like a function to distinguish repetitive styles or abstract complex codes. Especially, mixins without inputs can result in codes that are unnecessary without compression process like Gzip, so it warrants extra caution.

@mixin button($color) {
  background-color: $color;
  border-radius: 5px;
  padding: .25em .5em;
  &:hover {
    cursor: pointer;
	background-color: $color;
	border-color: $color;
  }
}
.button-a {
  @include button(#b4d455);
}
.button-b {
  @include button(#c0ffee);
}

The same can be done with the code below.

.button-a {
  background-color: #b4d455;
  border-radius: 5px;
  padding: .25em .5em;
}
.button-a:hover {
  cursor: pointer;
  background-color: #b4d455;
  border-color: #b4d455;
}

.button-b {
  background-color: #c0ffee;
  border-radius: 5px;
  padding: .25em .5em;
}
.button-b:hover {
  cursor: pointer;
  background-color: #c0ffee;
  border-color: #c0ffee;
}

Do not use extend directives.

@extends are not used because they are not intuitive and could cause problems when used with nested selectors. Using Gzip with Mixin produces the same benefits of @extend without the risks.

Only use nested selectors up to three levels.

After the third level, it is possible that the codes are too intricately involved with the HTML or that the codes are not reusable.

.wrapper {
  .container {
    .content {
      ...
    }
  }
}

Static Analysis Tools

Static Analysis Tools enable automation of tests to check for coding convention and proceeds to inspect possible erroneous patterns and basic structures. It is being widely adopted because of the fact that it punctually gives feedback synchronized with development tools. Also, it is easily customizable with easy addition and deletion of rules.

stylelint

Stylelint, as one of widely used CSS lint tools, supports up-to-date CSS syntax and allows easy addition of new rules. The following section will demonstrate the installing process and basic commands over the command line interface (CLI).

Install

Stylelint can be installed easily using the npm.

$ npm install –save-dev stylelint stylelint-config-recommended

Stylelint can be executed by adding to the package.json and using the npm commands.

"scripts": {
  "stylelint": "stylelint style.css"
}

Options

Options can be applied using .stylelintrc, stylelint.config.js, or package.json, and explanations regarding available rules can be found on the official user guide. It can also be used like ESLint, by only using predefined settings and applying only the extended version of desired rules.

  • stylelint-config-recommend: This is an extension module recommended by stylelint that contains possible errors. Additionally, this extension can be modified to suit personal needs and create a custom setting. If Prettier is installed, it is recommended to use stylelint-config-recommended.
  • Ex) Applying the stylelint-config-recommended and adding a new rule that only allows px as a unit of measurement.
module.exports = {
  "extends": "stylelint-config-recommended",
  "rules": {
    "unit-whitelist": ["px"]
  }
}

Usage

  • Ex) Creating a short CSS example file to check if using em units go against the lint rule
// style.css
p {
  border: 1em solid #ccc;
}

When the file is ran on CLI by typing $ npm run stylelint, it raises a Unexpected unit "em".

image

Afterword

This document was written to serve as an introduction to HTML, CSS, and Sass. This guide is based on the basic style rules used in FE Development Lab, and is subjected to change depending on the supporting browser, device, and frameworks. It is the hope of the author that this guide helps developers write consistent codes, cooperate easily with other developers, and minimize maintenance and expansion cost.


This document is an official Web Front-End development guide written and maintained by NHN Cloud FE Development Lab. Any errors, questions, and points of improvement pertaining to this document should be addressed to the official support channel (dl_javascript@nhn.com).


Last Modified
2019. 03. 29
FE Development LabBack to list