1. align-items — alignment along the cross axis
The align-items property is set on the flex container and controls the placement of items along the cross axis.
| Value | Behavior |
|---|---|
flex-start |
Pin to the start of the cross axis |
flex-end |
Pin to the end of the cross axis |
center |
Center along the cross axis |
baseline |
Align to the text baseline (useful when font-size differs) |
stretch (default) |
Stretch items to the full height of the container |
flex-direction:
column, the axes swap: the main axis runs top to bottom, and the cross axis runs left to right.2. justify-content — alignment along the main axis
The justify-content property controls the distribution of items and free space along the main axis.
| Value | Behavior |
|---|---|
flex-start (default) |
Pin to the start of the main axis |
flex-end |
Pin to the end of the main axis |
center |
Center the items |
space-between |
Outer items pinned to the edges, free space distributed evenly between items |
space-around |
Equal gaps around each item (outer gaps = half of the inner ones) |
space-evenly |
All gaps are perfectly equal (both between items and at the edges) |
Centering a modal window
.popup {
display: flex;
justify-content: center; /* horizontally */
align-items: center; /* vertically */
}
3. flex-shrink — shrink factor
The flex-shrink property determines whether an item inside a flex container can shrink when the screen size decreases.
Syntax
flex-shrink: 1; /* default — the item shrinks */
flex-shrink: 0; /* the item does NOT shrink */
The value is any positive number from 0 upward. The default is 1, meaning the item shrinks together with the container.
If you set 0, the item keeps its dimensions regardless of the screen width.
Practical example: a search bar
Imagine a search bar with three parts: a hint on the left, an input in the middle, and a “Search” button on the right. If you give all the items sizes in percentages, on small screens the hint will break onto extra lines and the button text will be clipped.
The solution: prevent the hint and the button from shrinking:
/* Hint and button — fixed */
.hint, .button {
flex-shrink: 0;
}
/* Input takes up the remaining space */
.search-input {
flex-grow: 1;
}
4. flex-grow — grow factor
The flex-grow property determines whether an item can grow to fill free space.
Syntax
flex-grow: 0; /* default — does not grow */
flex-grow: 1; /* grows, fills the free space */
flex-grow: 2; /* takes twice as much as flex-grow: 1 */
Unlike flex-shrink, different flex-grow values really do produce different results. An item with flex-grow: 2 will take twice as much free space as an item with flex-grow:.
1
Combining flex-shrink and flex-grow
For the search bar: the hint and the button neither shrink nor grow, while the input takes up all the free space:
.hint, .button {
flex-shrink: 0;
flex-grow: 0;
}
.search-input {
flex-grow: 1;
}
Now on any screen the left and right parts take up exactly as much room as their content needs, and the input fills the rest.
5. flex-basis and item sizing
The flex-basis property defines the initial size of an item in a flex container. It is the equivalent of width for horizontal flex containers and height for vertical ones.
Syntax
flex-basis: 250px;
flex-basis: 50%;
flex-basis: auto; /* size based on content */
Priority
flex-basis is not a hard width but a maximum preferred size. If there isn’t enough room in the container, the items will shrink.
flex-basistakes precedence overwidth— if you set both,widthis ignoredmin-width/max-widthtake precedence overflex-basis
In a vertical flexbox
If you set flex-direction: column, then flex-basis defines the item’s height (instead of its width). Similarly, height is ignored, while min-height / max-height have higher priority.
Rule
Do not mix flex-basis with width / height. Use either flex-basis, or width (height) together with the min-/max- variants.
6. The flex shorthand
The flex-grow, flex-shrink, and flex-basis properties can be written in shorthand via flex:
flex: <flex-grow> <flex-shrink> <flex-basis>;
Notation options
| Notation | Equivalent | Description |
|---|---|---|
flex: auto |
flex: 1 1 auto |
Grows, shrinks, size based on content |
flex: initial |
flex: 0 1 auto |
Default value: does not grow, shrinks |
flex: none |
flex: 0 0 auto |
Does not grow, does not shrink — fixed size |
flex: 1 |
flex: 1 1 0 |
Grows and shrinks proportionally |
flex: 2 |
flex: 2 1 0 |
Takes twice as much space as flex: 1 |
The second value — a number or a size?
The browser figures it out automatically: if the second value is a plain number (1, 2, 3), it’s flex-shrink. If it’s a value with units (px, %, rem), it’s flex-basis:
flex: 0 auto; /* flex-grow: 0, flex-basis: auto (flex-shrink: 1 by default) */
flex: 1 200px; /* flex-grow: 1, flex-basis: 200px */
Simplifying the search bar
.hint, .button {
flex: none; /* does not shrink, does not grow */
}
.search-input {
flex: 1; /* takes up all the free space */
}
7. Distributing space via flex
Instead of percentages, you can use flex to distribute space among items. The numbers define the proportions:
/* Three columns: 20% + 30% + 50% */
.col-1 { flex: 2; } /* 2/10 = 20% */
.col-2 { flex: 3; } /* 3/10 = 30% */
.col-3 { flex: 5; } /* 5/10 = 50% */
The sum of the flex values defines the “total number of parts.” Each item receives its share. For example, 2 + 3 + 5 = 10 parts, and the first item takes up 2/10 of the space.
Advantages over percentages
- Adding new items — the space is redistributed automatically
- The same
flex: 1for all — the items are always the same size - On mobile — you turn off
display: flex, and the blocks become ordinary divs that stack one below another
Responsive column layout
.container {
display: flex;
}
.main { flex: 2; } /* 2/3 of the space */
.sidebar { flex: 1; } /* 1/3 of the space */
@media (max-width: 768px) {
.container {
display: block; /* turn off flex — blocks stack one below another */
}
}
8. The order property
The order property changes the display order of items in a flex container without changing the HTML. It only works in flexbox.
Syntax
order: 0; /* default */
order: 1; /* display later */
order: -1; /* display earlier */
The value is any integer (positive or negative). The smaller the number, the earlier the item appears:
- In a horizontal flexbox: a smaller number is further left
- In a vertical flexbox: a smaller number is higher up
Example: a news card
On desktop: image → heading → tags → text. On mobile, the designer wants the tags below the text. The solution is to add order via a media query:
.card {
display: flex;
flex-direction: column;
}
@media (max-width: 480px) {
.card-tags {
order: 1; /* 1 > 0, so the tags move down */
}
}
On desktop all the items have order: 0 and are displayed in HTML order. On mobile the tags get order: 1 and move down.
HTML semantics
You can check the semantics of your markup by disabling CSS in the browser. Without styles, the page should read logically: headings are headings, lists are lists (not divs with spans), navigation is <nav> with <ul>.
9. align-content — aligning the rows of a flex container
The align-content property controls the distribution of rows in a multi-row flex container along the cross axis (works only with flex-wrap: wrap).
| Value | Behavior |
|---|---|
flex-start |
Rows pinned to the start |
flex-end |
Rows pinned to the end |
center |
Rows centered |
space-between |
Outer rows pinned to the edges, space between rows even |
space-around |
Equal gaps around each row |
stretch (default) |
Rows stretch to the full height of the container |
10. align-self — individual alignment of a flex item
The align-self property lets you override align-items for one specific flex item along the cross axis.
.container {
display: flex;
align-items: flex-start;
}
.button {
align-self: center; /* Only this button is centered */
}
| Value | Behavior |
|---|---|
auto (default) |
Inherits the container’s align-items value |
flex-start |
Pins to the start of the cross axis |
flex-end |
Pins to the end of the cross axis |
center |
Centers along the cross axis |
stretch |
Stretches to the full height of the row |
baseline |
Aligns to the text baseline |
Alternative: margin auto
You can also center a single item with margin-top: auto; margin-bottom: auto;, but align-self: center is a more explicit and semantic approach.
11. Centering items with margin: auto
In a flex container, margin: auto works both horizontally and vertically (unlike the normal flow, where margin-top: auto = 0).
.container {
display: flex;
width: 400px;
height: 400px;
}
.box {
width: 100px;
height: 100px;
margin: auto; /* Centers both horizontally and vertically */
}
margin auto in flex — distributing items
| Item 1 | Item 2 | Result |
|---|---|---|
margin-left: auto |
— | The item is pushed to the right |
margin-right: auto |
margin-left: auto |
The items move to opposite corners |
margin-right: auto |
margin-right: auto |
Both are pushed to the left |
margin-left: auto |
margin-left: auto |
Both are pushed to the right |
margin: auto for both |
Both are centered | |
margin: auto controls individual items, whereas justify-content controls all the items in the container at once.12. position: fixed — fixed positioning
position: fixed behaves almost like absolute, but with one key difference: the element stays fixed while scrolling.
What it shares with absolute
- Width/height are calculated by content (not 100% of the parent)
- The element is invisible to its siblings and parent
- You can set
width/heighteven on inline elements - Controlled via
top,right,bottom,left
Difference from absolute
| absolute | fixed | |
|---|---|---|
| Reference point | The nearest parent with position: relative (or the viewport, if there is none) |
Always the viewport (the browser window) |
| On scroll | Scrolls together with the page | Stays in place |
| Responds to a relative parent | Yes | No |
Typical examples of fixed
- A fixed header — always at the top while scrolling
- A modal window — covers the whole screen, doesn’t move
- A “Back to top” button — always in the corner of the screen
- A chatbot — a support window in the bottom right
- A side menu (dashboard) — fixed navigation on the left
/* Modal window — fixed instead of absolute */
.popup {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 9999; /* guaranteed on top of everything */
display: flex;
justify-content: center;
align-items: center;
}
13. display: none — hiding completely
display: none removes the element from the page entirely — as if it had been deleted from the HTML.
| Hiding method | Visible? | Takes up space? | Interactive? | Accessible to screen readers? |
|---|---|---|---|---|
display: none |
No | No | No | No |
opacity: 0 |
No | Yes | Yes (can be clicked) | Yes |
Visual hiding (clip-path) |
No | No | Yes (Tab) | Yes |
display: none— the element should not exist on the page (a closed modal window, a hidden tab)- Visual hiding (
clip-path) — the element is needed but not visible (text for screen readers, hidden labels)
14. Showing/hiding with classes (the popup pattern)
The classic approach for modal windows, dropdown menus, and tabs:
/* Base state — hidden */
.popup {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: none; /* hidden */
}
/* Active state — shown */
.popup--active {
display: flex; /* overrides display: none */
justify-content: center;
align-items: center;
}
How this works with JavaScript
// Close the popup when the close button is clicked
document.querySelector('.popup__close')
.addEventListener('click', function() {
document.querySelector('.popup--active')
.classList.remove('popup--active');
});
JavaScript only adds/removes a class. All the styling is in CSS.
.popup--active class must be lower in the CSS than .popup. With equal specificity, the browser applies whichever comes last.15. Specificity of CSS selectors
When several rules with the same property apply to one element, the browser chooses based on the specificity value.
The formula: 4 numbers
| Position | What we count | Example |
|---|---|---|
| A | Inline style (style="...") |
0 or 1 |
| B | Number of ids |
#header → 1 |
| C | Number of classes, pseudo-classes, attributes | .nav:hover → 2 |
| D | Number of elements (tags), pseudo-elements | div::before → 2 |
Calculation examples
| Selector | A | B | C | D |
|---|---|---|---|---|
span |
0 | 0 | 0 | 1 |
.slogan |
0 | 0 | 1 | 0 |
#slogan |
0 | 1 | 0 | 0 |
style="..." |
1 | 0 | 0 | 0 |
ul > .widget.socials |
0 | 0 | 2 | 1 |
.widget.socials |
0 | 0 | 2 | 0 |
div.widget:hover |
0 | 0 | 2 | 1 |
16. What does NOT affect specificity
- The child combinator
>— adds no weight - The adjacent sibling combinator
+— adds no weight - The general sibling combinator
~— adds no weight - The descendant combinator (space) — adds no weight
/* Both have a specificity of 0,0,2,1 */
ul > .widget.socials { ... }
ul .widget.socials { ... }
> “narrows” the selection, but does not raise specificity.17. Equal specificity → order in the CSS
If two selectors have equal specificity, the one located lower in the CSS file wins.
/* Specificity of both: 0,0,1,0 */
.slogan { font-size: 40px; } /* ← loses */
.text { font-size: 20px; } /* ← wins */
class="slogan
text" or class="text slogan". Only the order of the rules in the CSS matters.Practical application
That’s why third-party styles (libraries, widgets) are linked before your own styles:
<!-- 1. First — third-party styles -->
<link rel="stylesheet" href="widget.css">
<!-- 2. Then — your styles -->
<link rel="stylesheet" href="style.css">
Thanks to this, your selector with the same specificity will override the library’s styles.
18. The child combinator > and combining with a tag
Descendant vs child
| Selector | What it selects |
|---|---|
.widget div |
All divs inside .widget (at any nesting level) |
.widget > div |
Only the direct child divs (the first level) |
Combining a class with a tag
/* Any element with the class .card */
.card { ... }
/* Only <article> with the class .card */
article.card { ... }
div.card rigidly binds the styles to <div>. If you later change the tag to <article>, the styles will break. Style by classes.19. !important — when there are no other options
!important raises the priority of a specific property (not the whole selector) above inline styles:
span {
font-size: 10px !important; /* beats even style="font-size: 20px" */
}
If there are two !important declarations, the one with the higher selector specificity wins.
!important is an “anti-pattern.” It’s a sign of a flaw in your CSS architecture. It’s very hard to “dig out” of the code later.
- Don’t use it unless absolutely necessary
- If you did use it — always leave a comment explaining why
- Acceptable cases: a quick hotfix, overriding a third-party widget without access to its CSS
20. CSS architecture recommendations
- Style by classes, not by id (id is for JavaScript)
- As flat a structure as possible: one class → one selector (
.popup-box,.popup-close) - Avoid deep nesting:
.page .main .content h1is bad - Better:
.main-title— simple, clear, low specificity - Link third-party libraries before your own styles
- Don’t use
!importantin ordinary work
.popup, .popup__box, .popup--active all have a specificity of 0,0,1,0.21. Loading fonts — @font-face
The @font-face construct describes a font file for the browser. Each typeface style gets its own @font-face.
/* Regular style */
@font-face {
font-family: "PT Sans";
src: url("../fonts/pt-sans-regular.woff2") format("woff2");
font-weight: 400;
font-style: normal;
}
/* Bold style */
@font-face {
font-family: "PT Sans";
src: url("../fonts/pt-sans-bold.woff2") format("woff2");
font-weight: 700;
font-style: normal;
}
How the browser chooses a file
The browser compares three of the element’s parameters with the description in @font-face:
font-family— does it match?font-weight— does it match?font-style— does it match?
If all three match, the corresponding font file is loaded.
Fallback font (font stack)
body {
font-family: "PT Sans", Arial, sans-serif;
}
sans-serif or serif).| Family | Description | Example |
|---|---|---|
sans-serif |
Without serifs (grotesque) | Arial, Helvetica |
serif |
With serifs (antiqua) | Times New Roman, Georgia |
22. Reading text parameters from a layout
In Photoshop/Figma, for each text layer we read:
| Parameter | CSS property | Note |
|---|---|---|
| Font family | font-family |
PT Sans, Roboto, etc. |
| Style (Bold, Regular…) | font-weight |
400 = Regular, 700 = Bold |
| Font size | font-size |
Make sure the units are pixels |
| Line height | line-height |
If “Auto” → normal |
| Uppercase icon | text-transform: uppercase |
— |
| Color | color |
Eyedropper or the layer’s properties panel |
line-height for single-line headings! Designers often use line height for spacing. If the text becomes two lines, the line spacing will be too large. Set line-height: normal or pick a sensible value.23. Image formats
| Format | Type | Transparency | Compression | Used for |
|---|---|---|---|---|
| JPEG | Raster | No | Lossy | Photos, backgrounds, illustrations |
| PNG | Raster | Yes | Lossless | Logos, elements with transparency |
| SVG | Vector | Yes | Minimal size | Icons, graphics, simple illustrations |
| WebP | Raster | Yes | Better than JPEG/PNG | A modern alternative to JPEG and PNG |
24. Summary table
| Topic | Key point |
|---|---|
| align-items | Alignment along the cross axis: flex-start, center, flex-end, baseline, stretch |
| justify-content | Alignment along the main axis: center, space-between, space-around, space-evenly |
| flex-shrink | Shrink factor: 1 (default) — shrinks, 0 — doesn’t |
| flex-grow | Grow factor: 0 (default), 1+ — fills the free space |
| flex-basis | Initial item size; takes precedence over width/height |
| flex (shorthand) | flex: none — fixed; flex: 1 — fills the space; flex: auto — grows to fit content |
| Distributing space | flex numbers define the proportions: flex: 2 + flex: 1 = 2/3 + 1/3 |
| order | Change the display order without changing the HTML; a smaller number is earlier |
| align-content | Aligns rows in a multi-row flex container (flex-wrap: wrap) |
| align-self | Individual alignment of a flex item; overrides the container’s align-items |
| margin: auto in flex | Centers an item both horizontally and vertically; controls individual items |
| position: fixed | Like absolute, but stays fixed while scrolling; always relative to the viewport |
| display: none | Hides completely; the element “doesn’t exist” (takes no space, not interactive) |
| Popup pattern | .popup (display: none) + .popup--active (display:); JS adds/removes the class |
| Specificity | 4 numbers: inline / id / classes+pseudo-classes / tags+pseudo-elements |
| Equal specificity | The rule placed lower in the CSS wins |
| !important | Raises the priority of a single property; an anti-pattern, avoid it |
| The > selector | Child (first level); does not affect specificity |
| @font-face | Describes a font file; one @font-face = one typeface style |
| font stack | Always: custom → system → sans-serif/serif |
| Image formats | JPEG — photos; PNG — transparency; SVG — icons; WebP — a modern alternative |