1. Cross-browser compatibility and caniuse.com
Cross-browser compatibility is a site’s ability to display correctly across different browsers. It’s one of the most common problems web-layout developers face.
The caniuse.com service lets you check whether any CSS/HTML/JS technology is supported in modern browsers. You enter the name of a technology and see a full breakdown of support across browser versions.
Use cases:
- Checking support for CSS Custom Properties (variables)
- Checking support for
-webkit-line-clamp - Checking support for the
<picture>tag - Checking new CSS/JS features
2. Vendor prefixes
Vendor prefixes are special prefixes added to CSS properties that browsers used for experimental features:
-webkit-— Chrome, Safari, newer versions of Opera-moz-— Firefox-o-— older versions of Opera-ms-— Internet Explorer, Edge (older)
Originally these prefixes were created to showcase upcoming features. Over time developers began using them as full-fledged properties, which led to compatibility problems.
Example of a property with a vendor prefix:
.element {
-webkit-line-clamp: 3;
display: -webkit-box;
-webkit-box-orient: vertical;
overflow: hidden;
}
Today most properties are supported without prefixes. Check the current status on caniuse.com.
3. Truncating text: text-overflow and line-clamp
Single-line truncation with an ellipsis
To truncate text to a single line with an ellipsis, you need a combination of three properties:
.truncate {
white-space: nowrap; /* Prevent line wrapping */
overflow: hidden; /* Hide the overflow */
text-overflow: ellipsis; /* Add the ellipsis */
}
All three properties are required — without any one of them, the effect won’t work.
Multi-line truncation with -webkit-line-clamp
To truncate after a certain number of lines, use -webkit-line-clamp:
.multiline-truncate {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3; /* Number of lines */
overflow: hidden;
}
Despite the -webkit- prefix, this property works in all modern browsers.
4. BrowserHacks (CSS hacks)
BrowserHacks is a collection of CSS/JS workarounds for targeting a specific browser or version. These are special constructs with no logical meaning that only work in certain browsers.
Example of a hack for targeting Chrome:
/* Applies only in Chrome */
@media screen and (-webkit-min-device-pixel-ratio: 0) {
.element {
color: red;
}
}
Using hacks is not recommended — it indicates low-quality markup. If you still have to, always leave comments in the code.
5. Layout types: responsive, adaptive, fluid
Fluid layout (Fluid/Liquid)
The site stretches together with the browser window. All blocks have relative sizes (in percentages). An example is Wikipedia.
The problem: on very wide screens the text becomes hard to read — the lines are too long. Solutions:
- Limit the maximum width (
max-width) - Split the text into columns (CSS
columns)
Responsive layout (Responsive)
The site rearranges itself as the window width changes, using media queries. Elements change their position, size, and visibility. This is the most common approach.
overflow: hidden as a workaround
A common mistake — instead of finding the cause of horizontal scrolling, developers put overflow: hidden on the body. This only masks the problem rather than solving it.
6. CSS debugging with outline
A quick way to visualize the markup in order to find the cause of overflow or shifting — add an outline to all elements:
The outline method
/* Outline every element — doesn't affect sizes */
* {
outline: 1px solid red;
}
The semi-transparent background method
/* Each nested level becomes darker */
* {
background: rgb(0 0 0 / 0.1);
}
outline doesn’t affect block sizes and doesn’t shift neighboring elements — you see the real layout. border adds thickness and can change layout behavior.7. Practical example: a responsive user card
Let’s look at the markup for a card that, on desktop, shows the photo, name, job title, and contacts vertically, and on mobile — a full-screen photo with overlaid text.
HTML structure
<div class="card">
<img class="card__image"
src="photo.jpg"
alt="Photo">
<div class="card__info">
<h2 class="card__name">Jane Smith</h2>
<p class="card__post">Photographer</p>
<a class="card__contact" href="tel:+380501234567">+380 50 123 45 67</a>
<a class="card__contact" href="mailto:test@example.com">test@example.com</a>
</div>
</div>
Desktop version (object-fit: cover)
.card {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
text-align: center;
background: blue;
color: white;
}
.card__image {
width: 100px;
height: 100px;
object-fit: cover; /* Preserve proportions */
border-radius: 50%; /* Circle */
border: 4px solid white;
}
Mobile version (layers with position and z-index)
.card {
position: relative;
}
.card__image {
position: absolute;
width: 100%;
height: 100%;
object-fit: cover;
z-index: 1;
}
.card__info {
position: relative;
z-index: 2;
background: rgba(0, 0, 255, 0.5); /* Semi-transparent background */
}
The key technique: the image stretches across the whole block via position: absolute, while the text block with a higher z-index is overlaid on top with a semi-transparent background.
8. CSS triangles with border
When an element has zero width and height while its borders are large, each of the four borders forms a triangle:
.triangle {
width: 0;
height: 0;
border-width: 100px;
border-style: solid;
border-color: red green blue black;
}
To keep just one triangle, we make the remaining borders transparent:
/* Triangle pointing right */
.triangle-right {
width: 0;
height: 0;
border-top: 50px solid transparent;
border-bottom: 50px solid transparent;
border-left: 100px solid green;
border-right: 0;
}
Triangles are used for dropdown-menu arrows, tooltips, and composite shapes (trapezoids and the like). The technique is simple and requires no images.
9. Pill shape with border-radius
A pill button is an element with fully rounded ends. A common mistake is to use border-radius: 50%, which turns a rectangle into an oval.
/* Problem: 50% makes an oval, not a pill */
.oval {
border-radius: 50%; /* Oval shape */
}
/* Solution: a large value creates perfect rounding */
.pill {
border-radius: 100vw; /* Always a perfect pill */
}
The value 100vw is deliberately larger than any possible radius — the browser automatically clamps it to half of the element’s smallest dimension. The result is perfectly rounded ends regardless of width or height.
Shape variations
/* Dome (rounding only on top) */
.dome {
border-radius: 100vw 100vw 0 0;
}
10. Logical operators in media queries
Comma (OR — logical OR)
Combines several independent media queries. The styles apply if at least one of them is true:
@media (max-width: 600px), (orientation: portrait) {
/* Applies at a width up to 600px OR in portrait orientation */
}
and (logical AND)
Combines conditions — the styles apply only when all conditions are true at the same time:
@media (min-width: 768px) and (max-width: 1024px) {
/* Only for widths from 768px to 1024px */
}
not (logical NOT)
Negation — the styles apply to every case except the specified one:
@media not print {
/* For all devices except print */
}
In practice, the Mobile First approach with min-width is usually enough. Logical operators are rarely needed, but it’s useful to know they exist.
11. Full-screen background: background-size: cover
To create a full-screen block with a background image, use this combination:
.hero {
height: 100vh; /* The full window height */
background-image: url('hero.jpg');
background-size: cover; /* Fills the whole block */
background-position: center;
background-repeat: no-repeat;
}
cover guarantees the image fills the entire block without distortion, cropping the excess. contain, by contrast, fits the whole image but may leave empty strips.
For responsive mobile versions it’s better to use the <picture> tag, which lets you load different images for different screen sizes.
12. 100vh for full-screen sections
To create a block that fills the whole window height, developers use the vh unit:
.fullscreen-section {
height: 100vh; /* 100% of viewport height */
}
The 100vh problem on mobile devices
Mobile browsers have an address bar and a navigation panel that can hide as you scroll. The classic value 100vh always equals the window height without accounting for these panels — that is, it’s the largest possible viewport height. Because of this, on mobile 100vh can be larger than the visible area, and content spills off-screen.
New viewport units: svh, lvh, dvh
To solve this problem, three new viewport-height units were introduced in CSS:
| Unit | Name | What it means |
|---|---|---|
svh |
Small Viewport Height | Viewport height when all browser panels are shown (the smallest visible area) |
lvh |
Large Viewport Height | Viewport height when all browser panels are hidden (the largest visible area) |
dvh |
Dynamic Viewport Height | Changes dynamically as browser panels appear/disappear |
vh value behaves like lvh — it always equals the largest viewport size.How to choose the right unit
dvh— the best choice for full-screen sections (hero blocks, landing pages). Content always fits the visible area exactly, even when the address bar is shownsvh— guarantees content will never be cut off, even when the panels are maximally visible. Useful for elements where clipping is critical (for example, a login form or a CTA button)lvh— behaves identically to the classicvh. Rarely needed on its own, but can be useful if you deliberately want to use the maximum height
Usage example
/* Hero block that always occupies the entire visible screen */
.hero {
min-height: 100dvh;
}
/* Fallback for older browsers */
.hero {
min-height: 100vh; /* Older browsers */
min-height: 100dvh; /* Modern browsers */
}
dvh unit changes dynamically on scroll as browser panels hide. This can cause unwanted reflow of elements. So for animations and fixed elements it’s better to use svh or lvh, which have a stable value.Equivalent units for width
The same variants exist for viewport width too: svw, lvw, dvw. There are also the inline units svi, lvi, dvi and the block units svb, lvb, dvb, which account for the text direction. In practice these units are rarely needed for width, since browser panels usually affect only the height.
13. The modular grid
A modular grid is the division of space into equal columns against which content is placed. This concept comes from typography (newspapers, magazines, encyclopedias).
The standard is 12 columns, because 12 is divisible by 2, 3, 4, and 6:
| Division | Modules | Percentages |
|---|---|---|
| Into 2 parts | 6 + 6 | 50% + 50% |
| Into 3 parts | 4 + 4 + 4 | 33.33% + 33.33% + 33.33% |
| Into 4 parts | 3 + 3 + 3 + 3 | 25% + 25% + 25% + 25% |
| Sidebar + content | 3 + 9 | 25% + 75% |
| Content + sidebar | 8 + 4 | 66.67% + 33.33% |
Advantages of the modular grid:
- Easier to align elements — we think in modules, not pixels
- Percentages become clear: 6 of 12 = 50%, 4 of 12 = 33.33%
- Easier for the designer and the layout developer to communicate
14. Bootstrap: grid and components
Framework vs library
A framework (Bootstrap) is a comprehensive solution: a grid plus components (buttons, modals, carousels, accordions, and so on). A library (FlexboxGrid) solves a single task (for example, just the grid).
Adding Bootstrap
<!-- CSS -->
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@5/dist/css/bootstrap.min.css">
<!-- JS (for interactive components) -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5/dist/js/bootstrap.bundle.min.js"></script>
Container (container)
The container class creates a centered column with a fixed width for each breakpoint:
| Window width | Container width |
|---|---|
| < 576px | 100% |
| ≥ 576px | 540px |
| ≥ 768px | 720px |
| ≥ 992px | 960px |
| ≥ 1200px | 1140px |
| ≥ 1400px | 1320px |
Breakpoints
| Prefix | Minimum width | Name |
|---|---|---|
| (no prefix) | < 576px | Extra small |
sm |
≥ 576px | Small |
md |
≥ 768px | Medium |
lg |
≥ 992px | Large |
xl |
≥ 1200px | Extra large |
xxl |
≥ 1400px | Extra extra large |
Grid System
Bootstrap uses a container → row → col system:
<div class="container">
<div class="row">
<div class="col-8">8 of 12 modules (66.67%)</div>
<div class="col-4">4 of 12 modules (33.33%)</div>
</div>
</div>
Responsive columns
By adding a breakpoint prefix, we set a different number of modules for different devices:
<div class="row">
<!-- On sm and up: 8 modules, below sm: 100% -->
<div class="col-sm-8">Main content</div>
<div class="col-sm-4">Sidebar</div>
</div>
<div class="row">
<!-- Several breakpoints at once -->
<div class="col-12 col-sm-8 col-md-6 col-lg-4">
Responsive block
</div>
</div>
When to use Bootstrap
- Admin panels (where the design isn’t critical)
- Rapid prototyping to show a client
- Large portals with repeating components
- Projects where most Bootstrap components are genuinely needed
15. FlexboxGrid
FlexboxGrid is a lightweight library that implements only a modular grid based on Flexbox. Its classes are practically identical to Bootstrap: container, row, col.
Usage example:
<div class="row">
<div class="col-xs-12 col-sm-8 col-md-6 col-lg-4">
Content
</div>
</div>
Suitable when you only need a grid without components. For modern projects, also consider CSS Grid Layout as a native alternative.
16. Rules for naming CSS classes
What NOT to do
- Don’t name classes after colors:
.red-button→ better.button-dangeror.button-primary. The color theme may change - Don’t put tags in class names:
.div-header→ better.header. The tag may change - Don’t use sequential numbers:
.item-1,.item-2→ when adding new elements, the numbering loses its meaning - Don’t combine unrelated blocks:
.header, .footer, .sidebar { width: 100%;→ keep block styles separate
}
What TO do
- Use semantic names:
.card__image,.nav__link - One class = one selector: the best selector is a single class with no nesting
- Stick to a single separator style: if you use a hyphen — use it everywhere; if camelCase — everywhere
- The BEM methodology: Block__Element–Modifier — a proven approach to naming
- Move logic into HTML: instead of
:nth-child, better add a.item--firstclass
Simplicity of code
The simpler your CSS rules, the easier it is for other developers (and for you yourself later) to understand the code. Simple code is also processed faster by the browser.
17. Simplifying modifier classes
When you have a block with several style variants (modifiers), you usually use a base class plus a modifier class:
<button class="btn btn-primary">Save</button>
<button class="btn btn-danger">Delete</button>
<button class="btn btn-outline">Cancel</button>
Instead of writing separate styles for each modifier, you can use the [class|="btn"] selector, which selects every element whose class starts with the word btn:
/* Base styles for all btn-* */
[class|="btn"] {
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
}
/* Specific modifiers */
.btn-primary { background: #3b82f6; }
.btn-danger { background: #ef4444; }
.btn-outline { border: 1px solid currentColor; }
[class|="btn"] selector only works when btn is the first word in the class attribute. If an element has class="card btn-primary", this selector will not select it. For such cases it’s better to use [class*="btn-"].18. scroll-behavior: smooth
A CSS property for smooth scrolling when following anchor links:
html {
scroll-behavior: smooth;
}
After this, clicks on anchor links (for example, <a href="#section">) will smoothly scroll the page to the target element instead of jumping abruptly.
19. Site loading speed
The loading-speed formula:
Time = Number of files × File size + Processing complexity
How to influence each component:
- Number of files: combining CSS/JS files, using sprites/SVG instead of separate icons
- File size: minification, image compression, lazy loading
- Complexity: simpler CSS selectors, fewer animations, Critical CSS
20. Summary table
| Topic | Key point |
|---|---|
| caniuse.com | Checking technology support across browsers |
| Vendor prefixes | -webkit-, -moz-, -o-, -ms- |
| text-overflow: ellipsis | + white-space: nowrap + overflow: hidden |
| -webkit-line-clamp | Multi-line truncation with an ellipsis |
| Layout types | Fluid, responsive |
| CSS debugging with outline | * { outline: 1px solid red; } — see the boundaries of all elements |
| object-fit: cover | Filling a block with an image while preserving proportions |
| CSS triangles | border + width: 0 + height: 0 + transparent |
| Pill shape | border-radius: 100vw — perfectly rounded ends |
| Logical operators | Comma (OR), and (AND), not (NOT) |
| background-size: cover | Full-screen background without distortion |
| 100vh / dvh / svh / lvh | Viewport units: classic, dynamic, small, large height |
| Modular grid | 12 columns, came from typography |
| Bootstrap | Framework: grid + components |
| FlexboxGrid | Library: grid only |
| Class naming | No colors, tags, or numbers; BEM; simple selectors |
| Modifier classes | [class|="btn"] — shared styles for all btn-* |
| scroll-behavior | smooth — smooth scrolling to anchors |