Best Practices for Writing Clean and Maintainable Html, Css, and JavaScript Code
Kingsley Uwandu
Posted on June 3, 2024
Every developer should know this: Building well-structured and lasting websites require more than just functionality. For instance, you are tasked with the responsibility of adding a new feature to an existing website, or fix a bug in a critical application. But, in this situation when you get in to the codebase, you find a rough and scattered HTML, a difficult to understand CSS, and disorganized JavaScript - The code is hard to read and understand, let alone to modify; making you spend more hours trying to understand what each piece does. All these, only to realize that making any change risks affecting something else. This is many developer’s experience. They, at some point in their career, have experienced the frustration of dealing with unclean and poorly structured code. This does not just slow down development – it also leads to increase in bugs, high maintenance costs, and worst, a frustrating work environment.
However, all these can be avoided with some straight-forward and simple coding practices for writing clean and maintainable HTML, CSS, and JavaScript. Following these practices ensures efficient development, improves productivity, easier collaboration, and a long-term and better user experience.
Html Best Practices
a. Use Semantic HTML:
Semantic HTML involve using HTML tags that gives meaning and purpose of the content they enclose. Elements like <header>
, <nav>
, <article>
, <section>
, and <footer>
are semantic – They provide useful information to browser, search engines, and technologies that assists in the different roles of a webpage. This practice leads to:
Improved Accessibility: By providing meaning and context to the content structure, Semantic elements like
<header>
,<nav>
,<main>
,<article>
,<section>
, and<footer>
helps certain technologies, such as screen readers to interpret the page structure more accurately. This allows visually impaired users to understand the layout and navigate the content more efficiently. For example, a<button>
is recognized as a clickable button, and a screen reader will know this. This informs users who depend on these technologies that they can interact with the button using their keyboard.Help Users navigate a webpage: When navigating a webpage using the Tab key, the browser will move focus to interactive elements like links (
<a>
), form controls (<input>
,<button>
, etc.), and any element with atabindex
attribute. Tags like<button>
and<a>
are keyboard accessible. When you press Tab, the focus will automatically move to these elements, allowing users to interact with them using the Enter or Space keys.Keyboard Navigation: Semantic tags come with built-in keyboard behaviors. For instance, for
<button>
, pressing Enter or Space activates the button, and for<a>
(with anhref
attribute), pressing Enter follows the link.
Non-semantic elements (like<div>
or<span>
) do not have these built-in behaviors. You would need to add additional JavaScript to try this functionality, which likely is prone to error, and harder to maintain sometimes.
b. Logical Nesting of Elements:
Logical nesting ensures that elements are nested in a way that shows their relationships and hierarchies within the content. This practice helps maintain a clear and orderly document flow. This can be achieved in two ways:
-
Hierarchical Structure: Use elements in a hierarchical manner to indicate their level of importance and relationship to other content. For example, heading tags (
<h1>
,<h2>
, etc.) should follow a logical order.
<article>
<h1>Main Title</h1>
<section>
<h2>Subsection 1</h2>
<p>Content for subsection 1.</p>
</section>
<section>
<h2>Subsection 2</h2>
<p>Content for subsection 2.</p>
</section>
</article>
- Nesting child into parent: Proper nesting of child elements within their parent elements is one of the best practices for writing HTML code. It helps to improve the readability.
Parent element: An HTML element that contains other elements within its opening and closing tags.
Child element: An HTML element that is nested within another element (parent element).
For example:
<nav>
<ul>
<li><a href="#">Home</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Contact</a></li>
</ul>
</nav>
In the above code snippet, the <nav>
element is the parent element.<ul>
element is the child element of the <nav>
element. While the <li>
elements are child elements of the <ul>
element, the <a>
elements are child elements of the <li>
elements. The arrangement improves code readability, ensure easier maintenance, and reduce code error.
c. Indentation and Formatting:
Indentation and Formatting do not affect how the web browser interprets the code itself, but they provide some benefits to anyone who needs to read or modify the code. While Indentation creates a visual hierarchy, clearly showing how elements are nested within each other, Formatting, similar to paragraphs and spacing in written text, separates different parts of the code, making it easier to differentiate between elements, attributes, and content. Both practices improve code readability.
Example of indentation:
<div class="crown">
<h2>Section Title</h2>
<p>This is a paragraph within the section.</p>
</div>
Line Breaks: Line break is an example of formatting. Use line breaks to separate logical blocks of code, such as between different sections or elements. For example:
<header>
<h1>Website Title</h1>
</header>
<main>
<article>
<h2>Article Title</h2>
<p>Article content goes here.</p>
</article>
</main>
<footer>
<p>Footer content goes here.</p>
</footer>
d. Clear and Descriptive Naming:
Using clear and descriptive names for IDs and classes helps others understand the purpose of each element quickly. Choose meaningful names that explain the content or purpose of the elements they refer to.
<div id="main-header" class="header">
<h1>Website Title</h1>
</div>
<nav class="main-navigation">
<ul>
<li><a href="#">Home</a></li>
<li><a href="#">Services</a></li>
<li><a href="#">Contact</a></li>
</ul>
</nav>
Consistent Naming Conventions: Stick to a consistent naming convention. For example, if you are using kebab-case for classes and IDs, use throughout.
<div class="content-wrapper">
<section class="main-section">
<h2 class="section-title">Section Title</h2>
<p class="section-content">Content for this section.</p>
</section>
</div>
e. Separation of Files:
This means keeping different types of code separate. HTML code should be in Html file, stylings in Css file, and Javascript in Javascript files, which makes it easier to maintain and read the code.
HTML for Structure: Use HTML primarily for defining the structure and content of the web page, avoiding inline styles or scripts.
<article>
<h2>Article Title</h2>
<p>This is the content of the article.</p>
</article>
CSS for Styling: Keep CSS in separate files or within <style>
tags to handle the styling of the page. Example (external CSS file):
.article {
font-family: Arial, sans-serif;
color: blue;
}
JavaScript for Behavior: Place JavaScript in separate files or within <script>
tags to manage the behavior and interactivity. Example (external JavaScript file):
document.addEventListener('DOMContentLoaded', () => {
const article = document.querySelector('.article');
article.addEventListener('click', () => {
alert('Article clicked!');
});
});
f. Consistent Document Flow:
A consistent document flow ensures that the content is presented in a logical and orderly manner. Organize your HTML to flow from top to bottom. This shows the reading order better:
<header>
<h1>Website Title</h1>
</header>
<main>
<section>
<h2>Main Content</h2>
<p>This is the main content area.</p>
</section>
<section>
<h2>Secondary Content</h2>
<p>This is the secondary content area.</p>
</section>
</main>
<footer>
<p>Footer content goes here.</p>
</footer>
g. Comments and Documentation:
Adding comments and documentation helps explain the purpose and structure of the code, making it easy for future maintenance and collaboration. Use meaningful comments to explain the purpose of each sections. For example:
<!-- Main navigation menu -->
<nav>
<ul>
<li><a href="#">Home</a></li>
<li><a href="#">Services</a></li>
<li><a href="#">Contact</a></li>
</ul>
</nav>
<!-- Main content area -->
<main>
<section>
<h2>About Us</h2>
<p>Information about our company.</p>
</section>
</main>
These comments should be used to section and group related parts of the code, making it easier to navigate:
<!-- Header Section -->
<header>
<h1>Website Title</h1>
</header>
<!-- Navigation Section -->
<nav>
<ul>
<li><a href="#">Home</a></li>
<li><a href="#">Services</a></li>
<li><a href="#">Contact</a></li>
</ul>
</nav>
<!-- Main Content Section -->
<main>
<section>
<h2>About Us</h2>
<p>Information about our company.</p>
</section>
</main>
<!-- Footer Section -->
<footer>
<p>© 2024 Company Name</p>
</footer>
h. Keeping HTML DRY (Don't Repeat Yourself):
The DRY (Don't Repeat Yourself) principle helps in reducing repetition of code. It, like the other practices, improves readability, and make it easier to maintain as it makes the process more efficient and less error-prone.
Techniques to Keep HTML DRY
Reusable Components: Create reusable components for elements that appear multiple times on your site. Use technologies like templates or frameworks that support component-based architectures.
Server-Side Includes (SSI): Use server-side includes
to embed common elements (like headers, footers, and navigation menus) across multiple pages.
Client-Side Templating: Use client-side templating engines such as Mustache, Handlebars, or Pug to create reusable HTML templates that can be dynamically inserted into the page.
CSS Classes for Repeated Styles: Instead of repeating inline styles, use CSS classes to apply the same styles to multiple elements.
JavaScript for Dynamic Content: Use JavaScript to insert or generate repeated content, reducing the need to embed multiple instances of the same HTML.
Example using PHP Include
:
Step 1: Create Reusable Components
First, create separate PHP files for the common elements you want to reuse across multiple pages, such as headers, footers, and navigation menus.
Header File (header.php):
<!-- header.php -->
<header>
<h1>Website Title</h1>
<nav>
<ul>
<li><a href="index.php">Home</a></li>
<li><a href="about.php">About</a></li>
<li><a href="contact.php">Contact</a></li>
</ul>
</nav>
</header>
Footer File (footer.php):
<!-- footer.php -->
<footer>
<p>© 2024 Company Name. All rights reserved.</p>
</footer>
Step 2: Include These Components in Your Main Files
Use the include
or require
statement in your main HTML files to insert these components.
Home Page (index.php):
<!-- index.php -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Home</title>
</head>
<body>
<?php include 'header.php'; ?>
<main>
<h2>Welcome to Our Website</h2>
<p>Home page content goes here.</p>
</main>
<?php include 'footer.php'; ?>
</body>
</html>
About Page (about.php):
<!-- about.php -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>About</title>
</head>
<body>
<?php include 'header.php'; ?>
<main>
<h2>About Us</h2>
<p>Information about our company.</p>
</main>
<?php include 'footer.php'; ?>
</body>
</html>
The advantage of the above is, any changes made to these components will automatically show across all the pages that include them. Above all, it makes the entire code cleaner.
Css Best Practices
A. Using a CSS Preprocessor
CSS preprocessors like SASS (Syntactically Awesome Style Sheets) and LESS (Leaner Style Sheets) enable developers to write more maintainable, efficient, and modular CSS. These preprocessors add features to CSS that make it easier to manage styles for larger projects, including variables, nesting, mixins, and functions.
Benefits of Using a CSS Preprocessor
Variables: Store reusable values (e.g., colors, font sizes) in variables, making it easy to update them across your stylesheet.
Nesting: Nest CSS rules to show the HTML structure, making the stylesheet more readable and maintainable.
Mixins: Create reusable part of CSS that can be included in other rules, reducing repetition.
Functions and Operations: Use functions and perform operations (e.g., calculations, color manipulations) to generate dynamic styles.
Example with SASS
Setting up Variables:
Instead of repeating the same color value throughout your Stylesheet, you can define them as variables.
//variables.scss
$primary-color: #3498db;
$secondary-color: #2ecc71;
$font-stack: 'Hevetica, Ariel, san-serif';
Using Nesting:
Nesting allows to structure your css in a way that shows your HTML, improving readability.
// Style.scss
header {
background: $primary-color;
color: #fff;
nav {
ul {
list-style: none;
li {
display: inline-block;
margin-right: 10px;
}
}
}
}
Creating Mixins:
Mixins help you define reusable CSS snippets.
// mixins.scss
@mixin border-radius($radius) {
-webkit-border-radius: $radius;
-moz-border-radius: $radius;
border-radius: $radius;
}
// Using the mixin
button {
@include border-radius(5px);
background: $secondary-color;
color: #fff;
padding: 10px 20px;
border: none;
}
Functions and Operations:
You can use functions and perform operations to dynamically generate styles.
// functions.scss
@function calculate-rem($size) {
@return $size / 16px * 1rem;
}
// Using the function
body {
font-size: calculate-rem(16px); // 1rem
}
h1 {
font-size: calculate-rem(32px); // 2rem
}
Importing Partial Files:
Organize your SASS code into multiple files and import them into a main file.
// main.scss
@import 'variables';
@import 'mixins';
@import 'functions';
@import 'styles';
From the above, we see clearly that nesting provides a logical structure to styles, mixins reduce repetition of code, and functions enable dynamic and reusable styling. All these combine to write CSS that is cleaner, more modular, and easier to maintain.
B. Organizing and Structuring:
Organize your CSS into multiple files based on functionality to make it easy to maintain.
Example:
/* layout.css */
.header {
background-color: #333;
color: white;
}
.footer {
background-color: #f1f1f1;
color: #333;
}
/* typography.css */
body {
font-family: 'Arial, sans-serif';
}
h1 {
font-size: 2em;
}
/* forms.css */
input[type="text"] {
border: 1px solid #ccc;
padding: 10px;
}
C. Naming Conventions:
Use consistent naming conventions. For example, if you choose to use the BEM (Block Element Modifier), use throughout.
<div class="card card--featured">
<div class="card__header">Title</div>
<div class="card__body">Content</div>
</div>
.card {
border: 1px solid #ccc;
padding: 20px;
}
.card--featured {
background-color: #f9f9f9;
}
.card__header {
font-weight: bold;
}
.card__body {
font-size: 14px;
}
D. Commenting:
Add comments to document sections of your CSS.
Example:
/* Header Styles */
.header {
background-color: #333;
color: white;
}
/* Footer Styles */
.footer {
background-color: #f1f1f1;
color: #333;
}
E. Consistent Formatting:
Use a consistent formatting style (line break) for readability.
Example:
.header {
background-color: #333;
color: white;
}
.footer {
background-color: #f1f1f1;
color: #333;
}
F. Avoiding !important
:
Minimize the use of !important
to avoid specificity issues.
Example:
/* Avoid this */
.button {
background-color: blue !important;
}
/* Prefer this */
.button {
background-color: blue;
}
G. Efficient Styling with Selectors:
Use specific class and ID selectors for better performance to make it easier to maintain.
Example:
/* Prefer this */
.navbar {
background-color: #333;
}
/* Overly specific */
div.container .navbar {
background-color: #333;
}
Overly specific selectors can become difficult to maintain, make styles harder to override, and can cause unexpected behavior if another rule with slightly lower specificity tries to style the same element.
H. Use Utility Classes:
Create utility classes for common styles to promote reuse. A utility class in CSS is a single-purpose class that applies a specific styling rule or a small set of rules to an element.
Example:
/* Utility classes */
.text-center {
text-align: center;
}
.margin-top-small {
margin-top: 10px;
}
/* Usage */
<div class="text-center margin-top-small">Centered text with margin</div>
JavaScript Best Practices
a. Use let
and const
Instead of var:
let
and const
have block scope, which helps prevent common bugs related to variable hoisting and scope issues.
Example:
// Using let
let count = 1;
count = 2; // Valid
// Using const
const name = 'Robert';
// name = 'Kingsley'; // Error: Assignment to constant variable
b. Use Descriptive Variable and Function Names:
Choose meaningful names that clearly describe the variable's or function's purpose.
Example:
// Good
let userName = 'Donald';
function getUserAge(user) {
return user.age;
}
// Bad
let x = 'Donald';
function foo(u) {
return u.a;
}
c. Less and straightforward functions:
Functions should perform a single task. This makes them easier to test and maintain.
Example:
// Good
function add(a, b) {
return a + b;
}
function multiply(a, b) {
return a * b;
}
// Bad
function calculate(a, b) {
const sum = a + b;
const product = a * b;
return [sum, product];
}
d. Use Strict Equality (===) and Inequality (!==) :
Strict equality checks both value and type, preventing unexpected type coercion.
Example:
// Good
if (age === 18) {
console.log('You are 18 years old.');
}
// Bad
if (age == 18) {
console.log('You are 18 years old.');
}
e. Use Template Literals for Strings:
Template literals make it easier to include variables and expressions inside strings.
Example:
// Good
let name = 'Robert';
let greeting = `Hello, ${name}!`;
// Bad
let greeting = 'Hello, ' + name + '!';
f. Use Default Parameters:
Default parameters helps handle cases where arguments are not provided.
Example:
// Good
function greet(name = 'Guest') {
console.log(`Hello, ${name}!`);
}
greet(); // Hello, Guest!
// Bad
function greet(name) {
if (name === undefined) {
name = 'Guest';
}
console.log(`Hello, ${name}!`);
}
g. Use Array and Object Destructuring:
Destructuring helps extract values from arrays or properties from objects into different variables.
Example:
// Good
const user = { name: 'Robert', age: 25 };
const { name, age } = user;
const numbers = [1, 2, 3];
const [first, second] = numbers;
// Bad
const user = { name: 'Robert', age: 25 };
const name = user.name;
const age = user.age;
const numbers = [1, 2, 3];
const first = numbers[0];
const second = numbers[1];
h. Avoid Global Variables:
Global variables can lead to conflicts and bugs. Always declare variables within the scope they are needed.
Example:
// Good
function calculateSum() {
let sum = 0;
// code to calculate sum
return sum;
}
// Bad
let sum = 0; // Global variable
function calculateSum() {
// code to calculate sum
return sum;
}
i. Use Comments appropriately:
Comments should explain the why behind the code, not the what. They are useful for complex logic or important details.
Example:
// Good
// Calculate the sum of all items in the array
function calculateTotal(items) {
let total = 0;
items.forEach(item => {
total += item;
});
return total;
}
// Bad
function calculateTotal(items) {
// This function calculates the total
let total = 0;
items.forEach(item => {
total += item; // Add item to total
});
return total;
}
j. Use Array Methods Instead of Loops:
Methods like map, filter, and forEach are often more readable and concise than loops.
Example:
// Good
const numbers = [1, 2, 3];
const doubled = numbers.map(n => n * 2); // [2, 4, 6]
// Bad
const numbers = [1, 2, 3];
const doubled = [];
for (let i = 0; i < numbers.length; i++) {
doubled.push(numbers[i] * 2);
}
Conclusion
Follow these standards for HTML, CSS and JavaScript to create clean, maintainable and efficient code. Well-written code is easier for you and others to understand, but it also forms a good foundation for future development of the code. Well-written code saves time when debugging or wanting to add new features to any existing code. It makes it easier to collaborate with others
Posted on June 3, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.