Effect.Labs
Practical Guide
Web Design
Tips
16 tips explained simply to create beautiful, fast, and accessible websites for everyone
Design · Performance · Animations · CSS · Accessibility · SEO
Table of Contents
1 16 Web Design Tips
01Guide your visitor's eyeDesignp.3
02Speed up your image loadingPerformancep.3
03Make your animations smoothAnimationsp.4
04Change all your colors in one lineCSSp.4
05Make sure everyone can read your contentAccessibilityp.5
06Make your buttons finger-friendlyDesignp.5
07Help Google understand your pageSEOp.6
08Respect users who are sensitive to motionAccessibilityp.6
09Display the essentials firstPerformancep.7
10Adapt your sizes to every screenCSSp.7
11Show that the click workedDesignp.8
12Describe your images for those who can't see themSEOp.8
13Switch to dark mode automaticallyCSSp.9
14Prevent your page from jumpingPerformancep.9
15Create forms people actually enjoy filling outDesignp.10
16Make keyboard navigation visibleAccessibilityp.10
2 Pre-launch Checklists
6 printable checklists + notes sectionp.11
3 Tools and Resources
8 free tools + about Effect.Labsp.12
This guide is made for you
🎯
For all skill levels
Whether you've already edited CSS or never touched code, every tip starts from something concrete.
💡
Understand, don't just copy
An "In plain terms" block explains each concept with everyday analogies.
⚙
Customize it
Each tip tells you which values to change and what it actually does.
Tip
No need to apply everything at once. Essential = must-do. Important = strongly recommended. Bonus = to go further.
In plain terms: Think of a newspaper front page. The headline jumps out at you, then the subheadings, then the body text. On a website, it's the same. You use different text sizes to create a visual path. Visitors instinctively know where to look first, just like when you walk into a room and your eyes are drawn to the brightest light.
/* Main heading — the largest, the boldest */
h1 { font-size: 3rem; font-weight: 800; }
/* Subheading — one step down */
h2 { font-size: 2rem; font-weight: 700; }
/* Section title — even more subtle */
h3 { font-size: 1.5rem; font-weight: 600; }
/* Body text — normal size, generous line height */
p { font-size: 1rem; line-height: 1.7; }
Customize it
Change the font-size values to adjust the scale. The key is to maintain a clear gap between each level. If your h1 is 3rem, your h2 shouldn't be 2.8rem — too close, and the eye can't tell the difference.
In plain terms: Images are the heaviest files on a website. It's like shipping a 20-pound package when a 1-pound one would do. The WebP format (a lighter image format than JPEG, created by Google) reduces file size without losing quality. And with loading="lazy", images only load when the visitor scrolls to them — no need to download everything at once.
<!-- Image that only loads when you scroll to it -->
<img src="photo.webp" <!-- file in WebP format (lighter) -->
loading="lazy" <!-- loads on demand, not right away -->
decoding="async" <!-- doesn't block the rest of the page -->
width="800" height="450" <!-- reserves space (prevents jumps) -->
alt="Image description"> <!-- text for accessibility -->
Customize it
Convert your images to WebP with Squoosh (free, see p.12). For the first visible image on your page (the "hero"), remove loading="lazy" — it should load right away.
In plain terms: Your browser has two ways to animate an element. The first uses transform (the property that moves, scales, or rotates an element) and opacity (transparency: 0 = invisible, 1 = visible). It's like a highway — the GPU (the component that handles display) takes care of it, and it's fast and smooth. The second way uses width, left, etc. This forces the browser to recalculate the entire layout. It's a country road during rush hour.
/* Smooth — the GPU handles it, zero jank */
.card {
transform: translateY(-8px); /* moves up 8 pixels */
opacity: 0.9; /* slightly transparent */
}
/* Janky — the browser recalculates everything on each frame */
.card { top: -8px; width: 102%; } /* avoid this */
Customize it
Replace translateY(-8px) with the distance you want. Negative values = moves up, positive = moves down. For a zoom effect, use scale(1.05) instead of changing the width.
In plain terms: Imagine you're painting an apartment. If you write the color code on each wall, changing your mind means repainting everything. But if you label a single can "main color" and everyone uses that can, you just need to swap the can. CSS variables work exactly like that: you define your colors once, and the entire site uses them.
/* Define the "paint cans" once */
:root {
--main-color: #6366f1; /* purple — change here, changes everywhere */
--text-color: #1a1a2e; /* dark text */
--radius: 12px; /* rounded button corners */
}
/* Use the cans everywhere on the site */
.button { background: var(--main-color); }
Customize it
Replace #6366f1 with your brand color. Add as many variables as you want: --background, --border, --spacing. The idea is to never hardcode a value more than once.
In plain terms: Ever tried reading a light gray text on a white background in direct sunlight? Unreadable. Contrast is the difference in brightness between text and background. The greater the gap, the more readable it is. The rule: a ratio of at least 4.5. The higher the number, the better. Below 4.5, it's like reading pencil on gray paper. This affects everyone: people with reduced vision, but also you on an average screen in broad daylight.
/* Good contrast: white on dark (ratio 12.6) */
.text-ok { color: #ffffff; background: #1a1a1a; }
/* Bad: gray on gray (ratio 2.1) — unreadable */
.text-ko { color: #888; background: #666; }
/* Secondary text — the bare minimum */
.text-muted { color: #52525b; } /* ratio 7.2 on white */
Customize it
Check your color combinations with WebAIM Contrast Checker (see p.12). Enter your text color and background color — the tool tells you if it's readable. Aim for a ratio above 4.5 for body text.
In plain terms: On a phone, you tap with your finger, not a mouse. Your fingertip is about 44 pixels wide on a screen. If a button is smaller than that, you miss it and get frustrated. It's like a tiny light switch in the dark — you miss it every time. Both Apple and Google recommend a minimum of 44 pixels.
/* Comfortable button for touch */
.button {
min-height: 44px; /* minimum height = fingertip size */
min-width: 44px; /* minimum width */
padding: 12px 24px; /* generous inner spacing */
font-size: 1rem; /* readable text without zooming */
cursor: pointer; /* cursor changes to a hand */
}
Customize it
Increase the padding for wider buttons. 12px 24px = 12 pixels top/bottom, 24 left/right. For square buttons (icons), just use padding: 12px.
In plain terms: Imagine a book with no table of contents, no chapters, no headings. Impossible to navigate. Google reads your site like a book. HTML tags (header, main, footer) are your chapters. They say: "this is the menu," "this is the main content," "this is the footer." Without them, Google sees a soup of unstructured text.
<header> <!-- Header: logo and navigation menu -->
<nav>...</nav> <!-- nav = navigation area -->
</header>
<main> <!-- Main content — only one per page -->
<h1>Title</h1> <!-- Only one h1 per page, it's the title -->
<article>...</article> <!-- article = standalone content -->
</main>
<footer> <!-- Footer: legal info, links, contact --></footer>
Customize it
Make sure your page has a single <h1> that describes the main topic. The <h2> and <h3> tags should follow in order — no h3 before an h2.
In plain terms: Some people experience dizziness or migraines when there's too much movement on screen. On their phone or computer, they enable a setting called prefers-reduced-motion (a setting that says "I prefer less movement"). With 3 lines of CSS, your site detects this setting and disables animations automatically. It's like turning down the volume when someone has a headache.
/* Normal animation — a button that slides in */
.button { animation: slide-in 0.5s; } /* 0.5s = half a second */
/* If the user has enabled "reduce motion" */
@media (prefers-reduced-motion: reduce) {
.button { animation: none; } /* disable all motion */
}
Customize it
You can also replace animation: none with animation-duration: 0.01s if you want to keep the animation's final state without the motion. Apply this media query to all your animations.
In plain terms: It's like prepping ingredients before cooking. With preload (an instruction that tells the browser "prepare this file in advance"), you indicate which files are a priority. The font and the hero image load first — the visitor sees the page building quickly, even if the rest arrives later in the background.
<!-- In the <head> of your page, before everything else -->
<!-- Preload the font (displays right away) -->
<link rel="preload" href="font.woff2"
as="font" crossorigin>
<!-- Preload the hero image (visible immediately) -->
<link rel="preload" href="hero.webp"
as="image">
Customize it
Only preload 2-3 critical files (font + hero image). Too many preloads = none are prioritized. The crossorigin attribute (a technical attribute required for fonts) must be present, or it won't work.
In plain terms: Instead of writing 10 different rules for phone, tablet, and desktop, the clamp() function does the work on its own. You give it three values: a minimum, an ideal in vw (a unit that represents a percentage of the screen width), and a maximum. It's like a thermostat: you set "no lower than 64, ideal at 70, no higher than 75" — and it adjusts on its own based on the outside temperature.
/* minimum ideal maximum */
h1 {
font-size: clamp(2rem, 5vw, 4rem); /* 5vw = 5% of screen width */
}
.section {
padding: clamp(20px, 5vw, 80px); /* margins that adapt */
}
/* Small screen = 2rem. Large screen = 4rem. In between = adapts */
Customize it
Change the three values in clamp(): the first number is the minimum (mobile), the last is the maximum (large screen). The middle value in vw controls the transition speed. The higher the number, the faster it changes.
In plain terms: When you press an elevator button, it lights up to confirm "got it." On a website, it's the same. A button should react when you hover over it, when you click, and while loading. Without visual feedback, the visitor thinks "it didn't work" and clicks 5 times. The transition property makes the change gradual instead of an abrupt switch.
/* transition = smooth change over 0.2s */
.button { transition: all 0.2s; } /* 0.2s = fast and smooth */
.button:hover { /* when the mouse hovers over it */
transform: translateY(-2px); /* moves up slightly */
}
.button:active { /* at the moment of click */
transform: scale(0.97); /* shrinks slightly = "press" effect */
}
Customize it
Change 0.2s to adjust the speed: smaller = snappier, larger = smoother. Change -2px for more or less movement. scale(0.97) = shrinks by 3% on click — subtle but effective.
In plain terms: Alt text (the alt attribute) describes an image in words. Three groups need it. Visually impaired users whose screen reader reads the page aloud. Visitors with a slow connection who see the text before the image loads. And Google, which can't "see" photos but reads the text to understand what your page is about. A good alt = a short sentence describing what you see, as if you were telling someone over the phone.
<!-- Good: describes what you actually see -->
<img alt="Team of 5 developers around a screen">
<!-- Bad: too vague, describes nothing -->
<img alt="image"> <!-- useless -->
<img alt="team-photo.jpg"> <!-- that's the filename, not a description -->
<!-- Purely decorative image (background, border): empty alt -->
<img alt=""> <!-- the screen reader ignores it -->
Customize it
Write your alt text as if you were describing the photo to someone over the phone. Don't start with "image of..." — jump straight to what you see. Keep it under 120 characters. For decorative images, use alt="" (empty, not missing).
In plain terms: Many people enable dark mode in the evening to reduce eye strain. Their phone or computer has a setting called prefers-color-scheme (a setting that says "I prefer a dark background"). If you've already set up CSS variables (tip 04), you just need to override them in a special block. Your site switches automatically — like a screen that adapts to ambient lighting.
/* Default colors (light background) */
:root { --bg: #ffffff; --text: #1a1a1a; }
/* If the user prefers dark mode */
@media (prefers-color-scheme: dark) {
:root { --bg: #0a0a0f; --text: #f0f0f0; }
}
/* The body uses the variables — it adapts on its own */
body { background: var(--bg); color: var(--text); }
Customize it
Replace #0a0a0f with your preferred dark background. Avoid pure black #000000 — it's too harsh. A very dark gray is more comfortable. Remember to check contrast (tip 05) in both modes.
In plain terms: Ever clicked a link and at the last second the content shifted because an image finished loading? You clicked the wrong thing. It's like a stack of papers on a desk: if you add one in the middle, everything else shifts. The solution: reserve space for each image in advance with aspect-ratio (the width-to-height ratio), even before it loads.
/* Reserve space with a ratio (16:9 = video format) */
.image-container {
aspect-ratio: 16 / 9; /* reserves the correct proportions */
background: #f0f0f5; /* gray color while waiting for the image */
}
.image-container img {
width: 100%; height: 100%; /* fills the entire container */
object-fit: cover; /* crops neatly if needed */
}
Customize it
Change the ratio based on your images: 16 / 9 for video, 4 / 3 for classic photos, 1 / 1 for square. The color #f0f0f5 is the placeholder — choose a gray close to your background.
In plain terms: A good form is like a good salesperson: it guides without frustrating. Every field has a visible label above it (not just gray text that disappears when you click). Validation happens in real time — "invalid email" shows up right away, not after clicking Submit. And the autocomplete attribute lets the browser fill in fields automatically, like when your phone suggests your email address.
<!-- Visible label + field with auto-complete -->
<label for="email">Your email</label> <!-- always-visible label -->
<input type="email" <!-- email type = adapted keyboard on mobile -->
id="email" <!-- linked to the label by the same id -->
required <!-- required field -->
autocomplete="email" <!-- automatic suggestion -->
placeholder="you@example.com"/> <!-- example in gray -->
Customize it
Change the type based on the field: tel for a phone number (numeric keyboard on mobile), password for a password, url for a link. The autocomplete attribute also accepts name, tel, address-line1.
In plain terms: Many people navigate with the keyboard: Tab to move to the next element, Enter to click. They need to see which element is selected through a colored outline. Removing this indicator with outline: none is like turning off traffic lights in the middle of the night. The :focus-visible property shows the outline only when using the keyboard — not the mouse.
/* NEVER do this — it removes all indicators */
*:focus { outline: none; }
/* Best practice: visible outline for keyboard only */
:focus-visible {
outline: 2px solid #6366f1; /* 2-pixel purple outline */
outline-offset: 2px; /* 2px gap between element and outline */
}
Customize it
Replace #6366f1 with your brand color so the outline matches your design. Increase outline-offset if the outline is too close to the element. 3px or 4px gives more breathing room.
Pre-launch Checklists
Check each item before putting your site live
Design
Headings clearly visible and ordered
Consistent spacing between sections
Buttons respond to clicks
Clear, human-friendly error messages
Speed
No layout shifts (aspect-ratio)
Accessibility
Sufficient text/background contrast
Keyboard navigation works
Images described (alt text)
Visible labels on form fields
Visible outline on keyboard focus
SEO
Unique meta description per page
h1 > h2 > h3 hierarchy followed
Search engine rules (robots.txt)
Mobile
Tested on an actual phone
Text readable without zooming
Security
HTTPS active (padlock in address bar)
Forms protected against spam submissions
Free Tools
8 essential tools to apply the tips from this guide
Web.dev
Google's official guide for building fast, well-made websites. Packed with free tutorials.
web.dev
WebAIM Contrast Checker
Enter two colors and check if the contrast is sufficient (see tip 05).
webaim.org/resources/contrastchecker
PageSpeed Insights
Enter your site's URL and get a speed score with actionable advice.
pagespeed.web.dev
Squoosh
Compress and convert your images to WebP right in the browser. Free, no sign-up required.
squoosh.app
Google Fonts
Hundreds of free, optimized fonts. Copy-paste the code to integrate them.
fonts.google.com
Can I Use
Check if a feature works across all browsers (Chrome, Safari, Firefox...).
caniuse.com
WAVE
Scan your site to find accessibility issues automatically.
wave.webaim.org
Favicon Generator
Generate your site's icon for all devices (browser tab, bookmarks...).
realfavicongenerator.net
Discover Effect.Labs
The CSS and JavaScript effects library ready to copy-paste into your web projects
Try it for free
71 free effects · No credit card required · effect.labs
Effect.Labs — 2026 — All rights reserved. This guide is distributed for free.