HTML:
<!DOCTYPE html>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>Awesome website</title>

<meta name="description" content="Description of the page">

<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="icon" type="image/png" href="favicon.png">

<link rel="apple-touch-icon" href="favicon.png">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">

In the HTML file:

<meta name="msapplication-config" content="browserconfig.xml">

In the browserconfig.xml file:

<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
	<msapplication>
		<tile>
			<square70x70logo src="small.png"/>
			<square150x150logo src="medium.png"/>
			<wide310x150logo src="wide.png"/>
			<square310x310logo src="large.png"/>
		</tile>
	</msapplication>
</browserconfig>

<link rel="canonical" href="http://example.com/i/like/trains">

<html lang="en">

<html dir="ltr">

<!--[if IE]>
	<p class="browserupgrade">You are using an <strong>outdated</strong> browser.
	Please <a href="https://browsehappy.com/">upgrade your browser</a> to
	 improve your experience and security.</p>
<![endif]-->

BAD:

<body>
		<link rel="stylesheet" href="style.css">
</body>
<script src="h.js"></script>

GOOD:

<head>
	<link rel="stylesheet" href="style.css">
</head>
<body>
	<script src="h.js"></script>
</body>

<meta property="og:type" content="website">
<meta property="og:url" content="https://example.com/page.html">
<meta property="og:title" content="Content Title">
<meta property="og:image" content="https://example.com/image.jpg">
<meta property="og:description" content="Description Here">
<meta property="og:site_name" content="Site Name">
<meta property="og:locale" content="en_US">
<!-- Optional, but recommended -->
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">

<meta name="twitter:card" content="summary">
<meta name="twitter:site" content="@site_account">
<meta name="twitter:creator" content="@individual_account">
<meta name="twitter:url" content="https://example.com/page.html">
<meta name="twitter:title" content="Content Title">
<meta name="twitter:description" content="Content description less than 200 characters">
<meta name="twitter:image" content="https://example.com/image.jpg">

BAD:

<button state="on">Hi</button>

GOOD:

<button data-state="on">Hi</button>

<a href="http://example.com" target="_blank" rel="noopener noreferrer">My awesome link</a>


HTML5 Semantic Elements are used appropriately (header, section, footer, main...).


W3C Validator
Webhint
Lighthouse

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

<!DOCTYPE html>
<html>
	<head>
		<!-- ... -->
	</head>
	<body>
		<!-- ... -->
	</body>
</html>

BAD

<img src = "kasztan.webp">

GOOD

<img src="kasztan.webp">

BAD:

<meta http-equiv="x-ua-compatible" content="ie=edge" />
<img src="h.webp"></img>

GOOD:

<meta http-equiv="x-ua-compatible" content="ie=edge">
<img src="h.webp">

BAD:

<input type="text" disabled="true">
<input type="checkbox" value="1" checked="">

GOOD:

<input type="text" disabled>
<input type="checkbox" value="1" checked>

BAD:

<body>
	<p>h is the best letter

GOOD:

<body>
	<p>h is the best letter</p>
</body>

BAD:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"  
			  "http://www.w3.org/TR/html4/strict.dtd">

GOOD:

<!DOCTYPE html>

BAD:

<?xml version="1.1" encoding="UTF-8" standalone="yes"?>
			<!DOCTYPE html>

GOOD:

<!DOCTYPE html>



Fonts:
Webfonts are provided in WOFF2, WOFF or TTF

Total webfont size does not exceed 2MB


Webfonts are not loaded from Google Fonts, for Perfomace, Reliability and Privacy reasons.
Instead, fonts are loaded from local files.


In case a font does not load, or is still loading, you should fall back to a font provided by the system.
pzplUI has a fallback font stack built into it.
CSS:

The website should look & work the same on all devices.

BAD:

<style>
	body {
		line-height: 1.5;
	}
</style>
<h1 style="color: red;">Hello World</h1>

GOOD:

<link rel="stylesheet" href="style.css">
<h1>Hello World</h1>

BAD:

.example {
	display: -ms-grid;
	display: grid;
	-webkit-transition: all .5s;
	-o-transition: all .5s;
	-moz-transition: all .5s;
	transition: all .5s;
	-webkit-user-select: none;
	   -moz-user-select: none;
		-ms-user-select: none;
			user-select: none;
	background: -webkit-gradient(linear, left top, left bottom, from(white), to(black));
	background: -webkit-linear-gradient(top, white, black);
	background: -moz-linear-gradient(top, white, black);
	background: -o-linear-gradient(top, white, black);
	background: linear-gradient(to bottom, white, black);
}

GOOD:

.example {
	display: grid;
	transition: all .5s;
	user-select: none;
	background: linear-gradient(to bottom, white, black);
}

BAD:

body {
	line-height: 1.5;
	display: flex
}

GOOD:

body {
	line-height: 1.5;
	display: flex;
}

BAD:

body{
	line-height:1.5;
	display:flex;
}

GOOD:

body {
	line-height: 1.5;
	display: flex;
}

BAD:

body {
	line-height: 1.5; display: flex;
}
h1 {
	font-size: 3rem;
}

GOOD:

body {
	line-height: 1.5;
	display: flex;
}
			
h1 {
	font-size: 3rem;
}

body {
	line-height: 1.5; /* use anything from 1.25 to 1.75 */
}


Compared to links, @import is slower, adds extra page requests, and can cause other unforeseen problems.

BAD:

@import url("framework.css");
@import url("custom.css");

GOOD:

<link rel="stylesheet" href="framework.css">
<link rel="stylesheet" href="custom.css">

.myawesomeanimation {
	will-change: contents;
}

BAD:

.myanimation {
	will-change: contents;
}
			
.myanimation2 {
	will-change: contents;
}
			
.myResourceHeavyanimation {
	will-change: contents;
}

GOOD:

.myResourceHeavyanimation {
	will-change: contents;
}

BAD:

#myEl {
	font-size: 2rem;
}
			
#myEl2 {
	font-size: 2rem;
	color: red;
}
			
#myEl3 {
	font-size: 2rem;
}

GOOD:

#myEl, #myEl2, #myEl3 {
	font-size: 2rem;
}
			
#myEl2 {
	color: red;
}


Use the tools avalible in your browsers devtools.


Firefox & Chromium is good for 90% use cases.

Images:

You can use Squoosh for that


You can use Squoosh for that

<img src="image.webp" decoding="async" loading="lazy">

<img src="image.webp" alt="A theatre" decoding="async" loading="lazy">



JavaScript:

BAD:

<script>
	const h = "hhh";
</script>

GOOD:

<script src="script.js"></script>

"use strict";
const h = "h";

<noscript>This site works best with JavaScript enabled. 
Please enable JavaScript or update your web browser.</noscript>

<script src="h.js" defer></script>


BAD:

const theBestLetter = "h"
			
function logTheBestLetter() {
	console.log(theBestLetter);
}
			
if (5 > 4)
	console.log(theBestLetter);
}

GOOD:

const theBestLetter = "h";
			
function logTheBestLetter() {
	console.log(theBestLetter);
};
			
if (5 > 4)
	console.log(theBestLetter);
};

BAD:

const h = () => {
	console.log("Hi");
	return "h";
};
		
setInterval(function () { console.log("h"); }, 5000);

GOOD:

function h() {
	console.log("Hi");
	return "h";
};
		
setInterval(() => { console.log("h"); }, 5000);

BAD:

function h() 
{
	return "h";
};
			
if (true) 
{
	return "h";
} 
else if (false)
{
	return "e";
} 
else {
	return "a";
};

GOOD:

function h() {
	return "h";
};
			
if (true) {
	return "h";
} else if (false) {
	return "e";
} else {
	return "a";
};

// BAD:
document.getElementById("h");
// GOOD:
document.querySelector("#h");

Security:

HTTPS is avalible for all sites and external content


Make it avalible to users with older operating systems, or those that can't set a correct time on their computer.

X-XSS-Protection: 1; mode=block

Content-Security-Policy: frame-ancestors 'self'
X-Frame-Options: SAMEORIGIN

X-Content-Type-Options: nosniff

Referrer-Policy: no-referrer, strict-origin-when-cross-origin


In PHP just type
strip_tags($variable);

Performance:

No description



There should be no more than 10
cookies, each should not cotain more than 4096 bytes of data.


Minify JS and CSS, HTML is optional

<link rel="dns-prefetch" href="https://cdn.kasztan.art">

<link rel="preconnect" href="https://cdn.kasztan.art">

<link rel="prefetch" href="thebestletter.webp">

<link rel="preload" href="script.js">

BAD:

<script src="foo.js"></script>
<link rel="stylesheet" href="bar.css">

GOOD:

<link rel="stylesheet" href="bar.css">
<script src="foo.js"></script>

Try not to use any iframes. If you want to fetch data from an external file, you can use some PHP code:

echo file_get_contents("myFile.html");

or, if you are using pzplPHP:

echo Storage::get("myFile.html");

<iframe src="hello.html" loading="lazy"></iframe>


If it's possible, use SVG instead of other file formats for images


Use the cache-control header, if the resource will be updated just
add a ?v parameter to the request URL


Or simply, don't load polyfills at all.


If you want analytics, consider using something like Plausible of Fantom, which weight only ~1kb.

I found this code on my shools website the other day. It dns-prefetches a lot of websites that the
visitor won't even visit, therefore slowing down the website.
It also tries to prefetch it's own domain, which is completly useless.

<link rel="dns-prefetch" href="//zsp.lubochnia.pl">
<link rel="dns-prefetch" href="//www.wikom.pl">
<link rel="dns-prefetch" href="//zs-plubochnia.bip.wikom.pl">
<link rel="dns-prefetch" href="//uonetplus.vulcan.net.pl">
<link rel="dns-prefetch" href="//www.saferinternet.pl">
<link rel="dns-prefetch" href="//www.bobr.edu.pl">
<link rel="dns-prefetch" href="//pixchallenge.org">
<link rel="dns-prefetch" href="//codeweek.eu">
<link rel="dns-prefetch" href="//hourofcode.com">
<link rel="dns-prefetch" href="//www.szkolneblogi.pl">
<link rel="dns-prefetch" href="//iteatr.tvp.pl">
<link rel="dns-prefetch" href="//czytamy.org">
<link rel="dns-prefetch" href="//autorytet.org">
<link rel="dns-prefetch" href="//szkolaniepodleglej.zhr.pl">
<link rel="dns-prefetch" href="//bohateron.pl">
<link rel="dns-prefetch" href="//www.w3.org">
<link rel="dns-prefetch" href="//www.google.pl">
<link rel="dns-prefetch" href="//wikom.pl">

A solution in this case would be to remove all dns-prefetches, therefore speeding up the website.



Using a third-party CDN is less secure, less private, less reliable and slower.

Accessibility:

The most important functionality (such as navbars) should work without JS


Color contrast should pass WCAG AA.


Headings should be used in the right order - from h1 to h6.


The website should be tested with a tool, such as the one built into Firefox DevTools.


There should be always a label associated with a input.

SEO:

There should be a sitemap.xml file avalilble
<?xml version="1.0" encoding="utf-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
    <url>
        <loc>http://example.com/</loc>
        <lastmod>2006-11-18</lastmod>
        <changefreq>daily</changefreq>
        <priority>0.8</priority>
    </url>
</urlset>


There should be a robots.txt file avalilble
# www.robotstxt.org/

# Allow crawling of all content
User-agent: *
Disallow:


The website is added onto Google Search Console, Bing Webmaster Tools, and Yandex Webmaster

UI/UX:

By using a consistent grid system, all your subpages will feel the same.


By using a consistent grid system, all your subpages will feel the same.
One example of a color system is Nord


This will help eliminate confusion about what you are trying to say. The United States goverment has a website about this.


Use contrast to make your content more readable, and to distinguish parts of the UI


Optimal loading time should be 16-128ms.
If the delay is longer than that, you should explain to the user why is he waiting.


While toggles can be good in some cases, there are a lot of cases when a plain checkbox would be just better.
More reading


Pick the right font and weight, as well as colors and letter spacing.
More reading


Make sure to group certain elements together.
More reading


When users are presented with more options, the more time they need to have to make a decision.
More reading


Make it as simple as possible, but not simpler.
More reading


Users expect that your website will work in the same way as every other website.
More reading


Users are likely to remember an object if it is visually different from other objects.
More reading


Users usually can keep only 5-9 things in their memory at the same time.
More reading


Users find prettier products to be easier to use.
More reading

.editorconfig:



An example editorconfig file that I use:
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = false
indent_style = tab
indent_size = 4
trim_trailing_whitespace = true

[*.md]
trim_trailing_whitespace = false

[*.php]
insert_final_newline = true

PHP:

Seperate your logic from your HTML.

Before: index.php

<?php
	$connection = DB::connect("settings");
	$data = DB::fetch("mydata", $connection);
	shuffle($data);
?>
	
<h1>Hello <?= $data[0] ?></h1>
<p>foo <?= $data[1] ?></p>

After: index.php

<?php
	$connection = DB::connect("settings");
	$data = DB::fetch("mydata", $connection);
	shuffle($data);
	require_once "index.view.php"; // or view(); if you are using laravel or pzplPHP
?>

index.view.php

<h1>Hello <?= $data[0] ?></h1>
<p>foo <?= $data[1] ?></p>

namespace Website\Controllers;




PDO prevents SQL injection, and has a number of useful features

function hello(String $whoToGreet = "World") 
{
	echo "Hello, {$whoToGreet}";
}

<?= $myVariable ?>

<?php if ($myCondition) : ?>
	<p>Nice!</p>
<?php elseif ($mySecondCondition) : ?>
	<p>OK!</p>
<?php else: ?>
	<p>Neat!</p>
<?php endif; ?>
			
<?php foreach ($data as $key => $value) : ?>
	<?= $value ?>
<?php endforeach; ?>

function hello($whoToGreet = "World") 
{
	echo "Hello, {$whoToGreet}";
}

Laravel:
php artisan make:model litera

Route::get($uri, $callback);
Route::post($uri, $callback);
Route::put($uri, $callback);
Route::patch($uri, $callback);
Route::delete($uri, $callback);
Route::options($uri, $callback);

Webhint:



An example hintrc file that I use:
{
	"browserslist": [
		"> 1%",
		"last 2 versions"
	]
}

XML:
<?xml version="1.1"?>

<?xml version="1.1" encoding="UTF-8"?>

<?xml version="1.1" encoding="UTF-8" standalone="yes"?>

<!-- BAD -->
<letter>
	<name>h</name>
</letter>
<letter name="h" type="awesome" rating="435897/345" yes="y e s" somepropertyidkwhattowrite="abcdefghkjklmnopqrstuwxyz" />
			
<!-- GOOD -->
<letter name="h" />
	<letter>
	<name>h</name>
	<type>awesome</type>
	<rating>435897/345</rating>
	<yes>y e s</yes>
	<somepropertyidkwhattowrite>
	abcdefghkjklmnopqrstuwxyz
	</somepropertyidkwhattowrite>
</letter>

BAD

<?xml version="1.1" encoding="UTF-8" standalone="yes"?>
<post name="Lorem"/>
<post name="Ipsum"/>
<post name="Dolor"/>

GOOD

<?xml version="1.1" encoding="UTF-8" standalone="yes"?>
<posts>
	<post name="Lorem"/>
	<post name="Ipsum"/>
	<post name="Dolor"/>
</posts>

Other:

You can use Ignition for this.


Tabs provide smaller filesizes than spaces.


No description


(With the exception of PHP)