Web Development Documentation
A brief guide to responsiveness and accessibility in web design and development with HTML & CSS
Semantic HTML
-
Why semantic HTML tags?
For both users and developers, the semantic HTML tags are important when navigating a website.
- The main advantages of using semantic HTML tags:
- Content's keywords will be caught by search engines by improving the page's SEO
- Accessibility: visually impaired users utilize text to speach screen readers when navigating websites, semantic HTML tags are used by this TTSR as indicators providing an easier and more user friendly experience.
- Finding blocks of meaningful code is significantly easier than searching through endless
<div>
s with or without semantic tags - They suggest the the type of data that needs to be populated
- Semantic naming mirrors proper custom element and component naming
CSS Template
-
Reset box-model, margins and paddings
HTML tags come with default properties. That is one of the reasons why they are so useful. However, when adjusting layouts and whitespaces, these properties can result in unwanted behavior. In order to avoid all the undesired outputs this situation brings, most developers reset their box-model, margins and padding at the very top of all their CSS files.
/* Box sizing, reset margins & paddings */ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
The universal selector *, selects every element in the page. More on pseudo-elements ::before and ::after
-
Setup custom properties
Defining custom properties, such as fonts, gaps, and colors at the top the CSS file in
::root
can increase a developer's efficiency immensely.-
Using 2-3 different font families create lisible pages. A key feature in combining a serif and a sans‐font tweaking the contrast (lankiness and plumpness) between them. Two font families from different types look well together when they are not too similar or too divergent.
:root { /* Fonts */ --ff-serif: 'Noto Serif', Cambria, Cochin, serif; --ff-sans-serif: Lato, 'Lucida Sans', Verdana, sans-serif; --ff-mnsp: 'Roboto Mono', 'Courier New', Courier, monospace; }
-
Defining colors in HSL format but not in
hsl()
, allows the developer to easily adjust the opacity. It is also possible to take it a step further and define different hue, saturation, brightness and alpha values. Attention: This method prevents the developer to see these colors on the working CSS file.:root { /* Colors */ --clr-base: 300, 40%, 15%; --clr-accent: 10, 60%, 50%; }
-
A few reference outlines or borders help adjusting the layout.
:root { /* Reference outline & border * --ref-out-1: 3px solid red; --ref-out-2: 3px solid yellow; }
-
Setting up animation durations can save time when altering with durations and delays.
:root { /* Durations */ --dur: 200ms; --delay: 100ms; }
-
Using 2-3 different font families create lisible pages. A key feature in combining a serif and a sans‐font tweaking the contrast (lankiness and plumpness) between them. Two font families from different types look well together when they are not too similar or too divergent.
-
Setup the body
Defining wanted and unwanted behavior can be time saving during development and provide better user experience.
-
Setting
<body>
height to device height ensures that the whole screen area is being rendered (This is critical if you have a background image that covers the whole page) and prevents a footer on a short page to float up in the middle.body { min-height: 100vh; }
-
The default
line‐height
is 1.4, which can make a page difficult to read where the content gets a little crowded. Setting the line-height to 1.5-1.7 increase lisiblity of long documents.body { line-height: 1.6; }
-
If the content doesn't containt any tabular data, it is considered best practice to hide overflowing content in the x direction. This also prevents
image
s andfigure
s altering the layout.body { overflow-x: hidden; }
-
Test the custom properties and see how they work. This both ensures anything being displayed in color not contained in your palette or not in your selected font families.
body { font-family: var(--ff-sans-serif); background-color: hsl(var(--clr-gray-dark)); color: hsl(var(--clr-gray-light)); }
-
Setting
-
Disable animations
CSS transitions and animations are great ways to create more engaging website. However, not everyone browses a webpage the same way. In fact, some need to disable all animations entirely just to not get a terrible headache.
/* A11Y - Remove all animations, transitions and smooth scroll for people who've turned them off */ @media (prefers-reduced-motion: reduce) { html:focus-within { scroll-behavior: auto; } *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; scroll-behavior: auto !important; } }
For a more detailed CSS reset template, check out Andy Bell's blogpost.
Responsive Units
-
Why to avoid* absolute units
Absolute units are great during the learning process; but when it comes down to putting the knowledge into practice, they fail us developers at resposiveness. Absolute units might cause adding more than necessary media queries in order to provide smooth transitions through all screen sizes. At first it can feel strange to work with relative units and you don't feel like you know what you're doing, but after a short adaptation period, resposiveness becomes so much easier to deal with.
- * : There are a few situations in which absolute units don't interfere with your design intentions and actually improve consistency :
- Media queries work best with absolute units.
- Using px for borders, outlines and some lines created with pseudo-elements makes sure that you have the same thickness (width, height, weight) no matter the size. If you prefer, you can adjust this by adding 1-2 more media queries (for mobile, tablet & laptop, large screens) with different thickness values.
- Using relative units when using shadows and filters like blur can result in inconsistency.
-
Where to use which unit
Property Units Details font-size
rem
Rem unit is ideal for adjusting font sizes. They can be combined with viewport width ( vw
) or viewport height (vh
) units and theclamp()
function to create responsive font sizes.vw vh
1vw = 1% of the viewport width, 1vh = 1% of the viewport height. They enable fonts to shrink and expand based on the width or height of the device screen. margin
paddingem ch
When creating interactive elements such as buttons, using em and ch units can help the size and whitespace adjust to the text content contained, which creates consistency through all elements of the same type. rem
If whitespace is arranged with margins and paddings, em units can cause them to be too small or too big. Using rem will make sure to always refer back to the root element's font size (usually <html>).
width
Setting direct width and especially height is usually dangerous unless you're definitely sure you know what you're doing. Try using
heightmin-height
instead ofheight
whenever possible. Combinewidth
withmin()
andmax()
functions.ch
When setting up the width of text elements the ch unit is a life-saver. By adjusting the font-size with rems and vws and setting the width with ch, you ensure there is no text overflow in the x-direction. This can be especially helpful when building tables and forms. %
The same property of the parent element is multiplied with the percentage. When combined with grid and flexbox, percentages can help developers create fluid layouts. vw vh
If the desired outcome is to have the elements size remain proportional to the screen size, viewport width and viewport height can be used.
Flex & Grid
-
Flex
When learning responsive layouts, developers usually start with flexbox whose introduction was a great CSS update saving developers from
float
s in order to get fluidity on pages.This guide on flexbox written by Chris Coyier from css-tricks.com explains thoroughly how flexbox implementations work and this flexbox cheatsheet poster summarizes it up visually.
-
Grid
After flexbox came grid which opened up a whole new world of possibilities for layouts. Grids became popular fast as they provide consistency and fluidity without wrapping and overflowing. However, one thing that most developers struggle with at the early stages of their front-end roadmaps is switching from flexbox to grid; since grid requires more coding in general.
This guide on grid written by Chris House from css-tricks.com explains thoroughly how grid implementations work and this grid cheatsheet poster summarizes it up visually.
-
Combining both
Using flex only or grid only can bring more work load than anticipated. Combining them on the other hand, although might seem difficult and complicated first, lets developers build systematic layouts.
With
grid-template-areas
, 2-4 different layouts (mobile, laptop, desktop, large screen) are easily created. The children of the grid container then can be assigned flex. This way a general and steady layout is created while letting the content move in it's maximum space without causing any problematic behaviour./* Main grid */ body { display: grid; grid-template-areas: "navbar" "hero" "intro" "services" "about-us" "footer"; row-gap: 1.3rem; padding: 1rem; } /* Assign grid areas */ #navbar { grid-area: navbar; } #hero { grid-area: hero; } #intro { grid-area: intro; } #services { grid-area: services; } #about-us { grid-area: about-us; } #footer { grid-area: footer; } /* Flex containers */ #navbar, #hero, #intro, #services, #about-us, #footer { display: flex; } /* Media queries */ @media screen and (min-width: 750px) { body { grid-template-areas: "navbar navbar" "hero intro" "services services" "about-us about-us" "footer footer"; } } @media screen and (min-width: 1800px) { body { grid-template-areas: "navbar hero" "navbar intro" "navbar services" "navbar about-us" "footer footer"; } }
Pseudo* Classes & Elements
-
Pseudo‐classes
Pseudo‐classes define differents states of the element they are applied on. These states provide feedback to users when interacting with the webpage.
The main difference between a regular class and a pseudo‐class is their presence on the DOM. Pseudo‐classes cannot be modified with JavaScript since they are not part of the DOM.
Almost every webpage has at least one link, not styling them with pseudo‐classes aggravate the overall user experince.
a
— General stylinga:link
— Unvisited linka:visited
— Visited linka:hover
— When the primary pointing device is on the linka:active
— When the link is being clickeda:focus
— When the link is selected either by clicks or tabs (When a user navigates with the TAB key). It comes last for accessibility reasons, it has the most importance.
The order of pseudo‐classes is important:
/* Link states */ a { text-decoration: none } a:link { color: hsl(var(--clr-base)); } a:visited { color: hsl(var(--clr-base)); } a:hover { color: hsl(var(--clr-base)); border-bottom: 1px solid hsl(var(--clr-base)); filter: brightness(130%) saturate(150%); } a:active { color: hsl(var(--clr-base-light)); border-bottom: 1px solid hsl(var(--clr-base-light)); } a:focus { color: hsl(var(--clr-base)); border-bottom: 1px solid hsl(var(--clr-base)); filter: brightness(130%) saturate(150%); }
-
Pseudo‐elements
Pseudo‐elements allow CSS to add new elements to the HTML.
The main difference between a regular HTML element and a pseudo‐element is their accessibility the DOM. Pseudo‐elements are not part of the DOM, (even if they look like it when you inspect the page), therefore is not accessible by the DOM, and cannnot be altered with JavaScript.
They are useful in adding decorational elements, such as section dividers and bullet points.
/* Section dividers */ .main-section { position: relative; } .main-section::before { position: absolute; content: ""; width: 100%; height: 2px; background-color hsl(var(--clr-base)); }
If an HTML element has both pseudo‐classes and pseudo‐elements, the pseudo‐elements inherit those states.
* : Not accessible by the DOM
Custom States
-
Setting Data Attributes
A common way to style HTML elements based on user interactivity is to define different states in CSS and interchange among these states through the DOM. To access the same element(s) in the stylesheet and in the DOM, we can set custom data attributes on HTML elements:
<div class="box" data-color="red"></div>
Although it is not mandatory, starting with the prefix
data-
comes in handy when manipulating the DOM; hence it is common practice to use this syntax. -
Defining States
We can give any number of values to the custom data attribute. By using attribute selectors in CSS, we can style each one differently.
.box[data-color="blue"] { background-color: blue; } .box[data-color="red"] { background-color: red; }
If different properties are manipulated, developers should keep in mind the principle of cascading and reset modified properties.
-
Changing States
We can give any number of values to the custom data attribute. By using attribute selectors in CSS, we can style each one differently.
const box = document.querySelector(".box"); box.addEventListener("click", () => { const boxColor = box.dataset.color; if (boxColor === "blue") { box.dataset.color = "red"; } else if (boxColor === "red") { box.dataset.color = "blue"; } });
The
data-
prefix allows us to use.dataset.attr-name
to update the DOM element. If thedata-
prefix is not used, the attribute can still be accessed with.getAttribute("attr-name")
(which has the value "attr-state") and updated with.setAttribute("attr-name", "new-attr-state")
.If different properties are manipulated, developers should keep in mind the principle of cascading and reset modified properties.