https://academind.com/community
⌛CSS 1: 1996 → CSS 2: 1998 → CSS 3: currently in development → split in modules, working on them separately (there won’t be CSS 4)
https://developer.mozilla.org/en-US/docs/Web/CSS/Reference
➡️Rule (Code line) = Selector + Declaration (Property + Value)
h1 {color: red; }
- Selector: h1, div, .blog-post, #main-title, [disabled], * (see below)
- Properties: background-color, width, margin, etc.
- Values: 30px, 50%, red etc. (might be also functions — like transform, or url).
- Inline styling — adding style right in the same line where the element is created
- Style tag — adding css in head within
<style></style>
tag - Using extra stylesheet:
<link rel="stylesheet" href="***.css">
. This is much better, because this way it’s clearly separated from html, which is easy to control; plus browser can cash those files separately and doesn’t need to redownload the file every time which is faster.
-
Elements. Set equal style for same elements.
<h1>**Our header**</h1> <p>The Blog Post</p> <div>More Info</div>
h1 { color: red; }
-
Classes. Set equal style for elements within the same class.
<h1 class="blog-post">**Our header**</h1> <p class="blog-post">**The blog post**</p> <div class="blog-post">**More info**</div>
.blog-post { color: red; }
-
Universal. Selects ALL elements on the page. Rarely use this one!
<h1>**Our header**</h1> <p class="blog-post">**The blog post**</p>
* { color: red; }
-
IDs. Set style to one specific element.
<h1 id="main-title">**Our header**</h1>
#main-title { color: red; }
-
Attributes. Set equal styles to all elements with attribute(s). This can be even used if element has an attribute like id=””, so one can use [id] selector (which is not the best practice, however)
<button disabled>Button</button>
[disabled] { color: red; }
Typically, *** selector** is not super efficiently rendered by browser. So to stylethe whole site text, is better to use <body>
.
https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Combinators
Allows to combine multiple selectors not to repeat or create too many of them, in a dependent way. It’s not inheritance! Just setting the style for specifically selected element.
➡️Combinators usually perform slightly worse (slower, than directly selecting certain elements like ID).
-
div + p {}
— adjacent sibling. Elements share the same parent. 2nd element comes immediately after the 1st element (so in this case, each<p>
which follows directly<h2>
)<div> <h2>Not applied</h2> <p>**CSS applied**</p> <h2>Not applied</h2> <h3>Not applied</h3> <p>Not applied</p> <h2>Not applied</h2> <p>**CSS applied**</p> </div>
-
h2 ~ p {}
— general sibling. Elements share the same parent. 2nd element comes after the 1st element, no matter immediately or not.<div> <h2>Not applied</h2> <p>**CSS applied**</p> <h2>Not applied</h2> <h3>Not applied</h3> <p>**CSS applied**</p> </div>
-
div > p {}
— child. Elements share the same parent. Second element is directly nested into the first element (in this case, all<p>
that are children of<div>
).<div> <div>Not applied</div> <p>**CSS applied**</p> <div>Not applied</div> <article> <p>Not applied</p> </article> <p>**CSS applied**</p> </div>
-
div p {}
— descendant. Second element is a descendant of the first element, no matter if they are direct children or not.<div> <h2>Not applied</h2> <p>**CSS applied**</p> <h2>Not applied</h2> <h3>Not applied</h3> <p>Not applied</p> <h2>Not applied</h2> <p>**CSS applied**</p> </div>
Interesting example of using combinators. E.g. we have a list <ol>
with many items <li>
inside. In order to select ALL items BUT the first one, we can use li + li
combinator (because all items but first one stay after anothe li
item).
-
Specificity (is used to resolve conflicts if multiple properties are applied to the same element)
- Inline styles (better not use)
ID
<class>
,<pseudo-class>
and[attribute]
<tag>
and::pseudo-element
*
(universal selector, used rarely)
-
Order. The latest style applied is more important, provided the rules have the same specificity.
The easiest way is to imagine DOM model as it shows in Dev tools in the browser, reading it from top to bottom — that’s why they are “cascading” styles. The actually applied styles are on top.
Within the same specificity level, the latest assigned style wins.
The inherit
property in CSS is used to explicitly inherit the value of a property from the parent element. By default, some properties automatically inherit (like color
and font
), while others do not (like margin
, padding
, border
).
When to Use inherit
?
- When you want child elements to match a parent’s style explicitly.
- When a property does not inherit by default (e.g.,
border
,padding
,margin
). - To override a default style in a CSS framework.
Property | Description |
---|---|
inherit |
Takes the value from the parent element. |
initial |
Resets to the CSS default (not the parent’s value). |
unset |
Acts like inherit for inherited properties and initial for non-inherited ones. |
Box = Element (content) + Padding + Border + Margin
Margin collapsing happens when two vertical margins meet, and instead of adding together, the larger margin is used:
- If we have two
<div>
elements, each with amargin-bottom: 20px
andmargin-top: 30px
, we might think they will add up to 50px spacing. But instead of 20px + 30px = 50px, CSS only keeps the larger margin (30px). Final space between them: 30px (not 50px). - If a child element has margins, and the parent has no padding or border, the child’s margin escapes (applies to the parent instead). E.g. if parent has
margin: 50px;
and childmargin-top: 30px;
— final space above parent: 50px (not 50px + 30px). - If some (zero) element has no content, no padding, no border and no height — then top and bottom margin will be merged into a single margin. The bigger one wins.
Important points:
- This does NOT happen with horizontal margins.
- Can be prevented using padding or border (e.g.
padding: 1px;
), oroverflow: hidden;
. - It was done to prevent double spacing. Without margin collapsing, the space between paragraphs with the same styles might be doubled. Also without collapsing, margins would stack unpredictably in different elements — you would have to manually adjust margins for every case.
- *Tag
body
has some margins by default (one usually overrides it).
To center element inside another element we can use margin: auto;
or margin: 0 auto;
/ margin: auto 0;
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_box_model/Mastering_margin_collapsing
Combine values of nultiple properties in a single property, e.g:
border: 2px dashed orange;
margin: 0 2px 4px 0;
ormargin: 0 5px;
Typically you set a general value as a shorthand, and later override one part of it if needed (e.g. we set a 1px grey border for all, and later for one element override one border to be blue and thick, for example).
In browser you can see in dev tools from which properties the value is combined (even if you haven’t set explicitely all of them):
Width
-
Block elements (unlike inline) usually have width 100% by default (body included).
-
Width is counted for the inner content size by default (
box-sizing: content-box;
), which doesn’t include border, margin and padding. So if it’s set to 100%, and we add all those parameters to that, and the block can get out of the screen. -
We can change it to
box-sizing: border-box;
(then all the parameters are counted as content + border + padding, but without margin). So that it’s a real “box”. It’s used really commonly. But you can’t inherit it by just adding to the body because all children that are blocks in body, will be overriden to content-box automatically, and you’ll have to apply it to every child.- So this is that rare case when we use universal selector (*) to reset it everywhere.:
* { box-sizing: boder-box;}
- So this is that rare case when we use universal selector (*) to reset it everywhere.:
Height
- For height: if we set it to 100%, it occupies only as much as its parent container allows it to be; but the parent containes usually takes as much as its children need it to be → sort of an infinite loop.
- That’s why if you want to have an effect, the parent container should also change the height of all higher level parents including html tag:
html, body, main /* parent1, parent2 ... */ {height: 100%;}
Element behaviour: inline
, block
, inline-block
, none. It’s how they are positioned in a flow of elements.
Display Type | Width & Height | Padding & Margin | Starts New Line |
---|---|---|---|
inline |
❌ Ignored (fits content) | ❌ Limited (only left/right) | ❌ No |
inline-block |
✅ Respects width/height | ✅ Yes (all sides) | ❌ No |
block |
✅ Takes full width | ✅ Yes (all sides) | ✅ Yes |
- When element is set to
none
, it’s taken out of the flow entirely — not visible anywhere but in DOM when you inspect the page. It’s used in combination with JS, when you have e.g. modal window that appears after clicking a btn. - If you only want to hide an element but you want to keep its place (i.e. other elements don't fill the empty spot), you can use
visibility: hidden;
- HTML: Block-level vs Inline Elements
- Block-level elements are rendered as a block and hence take up all the available horizontal space.
- E.g.:
<div>
,<section>
,<article>
,<nav>
,<h1>
,<h2>
,<p>
etc.
- E.g.:
- Inline elements on the other hand only take up the space they require to fit their content in. Hence two inline-elements will fit into the same line (as long as the combined content doesn't take up the entire space in which case a line break would be added).
-
They also use the box-model, but
margin-top
andmargin-bottom
have no effect;padding-top
andpadding-bottom
work differently — they don't push the adjacent content away but they will do so with the element border. Plus, setting awidth
orheight
are set to auto to take as much space as required by the content.https://hacks.mozilla.org/2015/03/understanding-inline-box-model/
-
E.g.:
<a>
,<span>
,<img>
-
- Block-level elements are rendered as a block and hence take up all the available horizontal space.
A bug to be aware of: if 2 elements are set to be inline-block, the white space between them right inside html counts to the width in css.
In such case we could either remove that white space, so that it doesn’t count:
Or we could calculate the width, distracting some white space out of it, like width: calc(100% - 10px);
for example. Or simply set it to 99%.
The vertical-align
property controls how inline or inline-block elements are aligned vertically relative to their container or surrounding text.
Pseudo classes
https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes
:class
Define the style for a specific state of the element.
Interesting example is the last sibling in a list of equal siblings: div:last-of-type {}
. We can also combine 2 pseudo classes like this: div:last-of-type:hover {}
Pseudo elements
https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements
::element
Define the style for specific part of the element.
-
Multiple classes on one element
<div class=”class1 class2”></div>
- If both classes target the same property, the one that appears later in the CSS file is applied (because of the cascade rule).
- If one class is more specific than the other, then it wins. E.g.
div.box
is more specific than.box
;
-
Combined selectors can include elements and classes together, e.g.
a.class1 {}
. This will select only anchor tags that have class1.- If we set it with a space between though, like
a .class1 {}
, it would target elements that are children (direct or indirect) of anchor tag and have class1 applied. - The same can be done to ID:
a#name1
(target all anchor tags that have this ID; although it’s not the common way to do it)
- If we set it with a space between though, like
-
When use what? Classes are used usually only to apply certain styling: they are reusable, but typically are not widely used in conjunction with JS; IDs are used once per page: and they often have not only styling purpose (e.g. link to this exact element on the page — e.g.
www.url/home#name
will direct user to the element withid = “name”
.)
color: red !important;
Typically it’s not smth that’s often used, because it breaks the rules of CSS. If you get important annotation twice, the 2nd one wins (cascading rule).
https://css-tricks.com/when-using-important-is-the-right-choice/
Allows to exclude a certain selector, e.g. :not(p)
will select any element that’s not a paragraph.
- The ability to exclude more than 1 selector is still an experimental feature, not supported by all browsers.
- We could also combine this class with other selectors, like
a:not(.class1) {}
https://developer.mozilla.org/en-US/docs/Web/CSS/:not
This class is slightly worse in terms of complexity of reading and understanding the CSS, which also reflects on performance — so this should be used with caution.
- Document flow — a normal order of the document elements; By default all elements have
position: static;
- Other options change the position of the element relative to its initial position or relative to the viewport/html/body;
position: relative;
- acts like static, until
top
/bottom
/right
/left
assigned — otherwise, nothing happens; - element appears on top of other static elements, covering them;
- acts like static, until
position: absolute;
- completely removes the document out of the flow;
- if none of the parents of the element have
position
applied, then the parent ishtml
; if one of them has, the closest parent that has this property, is a starting point to count- Oftentimes, parent is assigned
position:relative;
for such purpose;
- Oftentimes, parent is assigned
- after applying
top
/bottom
/right
/left
— moves respectively to a parent; - the element starts behaving like
inline-block
— no matter what initial element was (inline or block)
position: fixed;
- completely removes the document out of the flow; similar to absolute in this sense, but doesn’t move with the page when scrolling;
- position depends on the viewport, not parents;
- the element starts behaving like
inline-block
— no matter what initial element was (inline or block)
position: sticky;
https://www.youtube.com/watch?v=NzjU1GmKosQ- in an essence is a hybrid of relative and fixed; but the difference is that the element is not removed out of the flow, but still sticks to the top of the viewport;
- after applying
top
/bottom
/right
/left
— fixed behaviour works from the beginning of the parent element and to the end of it;
https://www.youtube.com/watch?v=jx5jmI0UlXU
https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/CSS_layout/Positioning
https://developer.mozilla.org/en-US/docs/Web/CSS/position
Position | Behavior | Moves relative to | Removes from normal flow? |
---|---|---|---|
static |
Default position (no special behavior). | — (default flow of the document) | ❌ No |
relative |
Moves relative to its original position. | Itself (original position remains reserved) | ❌ No |
absolute |
Moves relative to the nearest positioned ancestor (if none, relative to <html> ). |
First non-static ancestor | ✅ Yes |
fixed |
Stays fixed relative to the viewport (doesn’t move when scrolling). | Viewport | ✅ Yes |
sticky |
Acts like relative but becomes fixed when scrolling reaches a threshold. |
Its nearest scrollable ancestor | ❌ No (until it sticks) |
- Is equal to
0
by default, but doesn’t have any impact for elements which don’t have position property applied specifically (static
by default); - If 2 elements have position applied, the one that comes later in hrml, is placed above.
- z-index is applied first to parent elements, and even if the children elements of this parent have larger index than the parent above them, it’ll not apply.
https://developer.mozilla.org/en-US/docs/Web/CSS/z-index
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_positioned_layout/Stacking_context
hidden
property hides children of the parent;- However, if we apply it for the
body
element, it won’t work — we’ll need to also add it to thehtml
element (or addoverflow: auto;
to html element explicitely);- The
<html>
element (not<body>
) is the root of the document and usually dictates the scrolling behavior. - Many browsers apply
overflow: auto
to<html>
by default, making<body>
inherit its behavior.
- The
https://developer.mozilla.org/en-US/docs/Web/CSS/background
-
Properties used
-
background-image
-
background-size
background-size: 100px;
height is adjusted according to aspect ratio100px 200px;
image will be distorted50%
just takes 50% of width, height is adjustedauto 100%
width will adjusted, but height always occupies all the spacecover
keeps aspect ratio of the image; and automatically makes image cover the whole container you needcontain
makes sure that the entire image is shown, whatever container allows
-
background-position
px
— left edge of the image positioned relatively to the left top corner of the container, by x and ycenter
/left top
/left bottom
etc.%
— likecover
property, but % define how the image will be cropped; e.g. if we say10% 20%
, then 10% of the space that might be cropped by x axis, will be at the left, and 20% that can be cropped by y axis — on the top.left 10% top 20%
-
background-repeat
: can be set only to one axis (x or y) -
background-origin
,background-clip
whether the background extend beneath a border (border-box; context-box) — with or without border and padding; 2 properties define that behaviour for x and y axes;Property What it Controls Common Values Purpose background-origin
Where the background image starts border-box
,padding-box
,content-box
Affects positioning of the background image background-clip
How far the background is visible border-box
,padding-box
,content-box
,text
Affects clipping (cropping) of the background They work together to control the placement and visibility of background images.
-
background-attachment
how does scrolling behave -
background-color
doesn’t override image, even if stays after it;
-
-
Shorthand:
background: url(”…”) left 10% bottom 20%/cover no-repeat border-box content-box;
— position/size, repeat, origin, clip (if only one prop — applies to both origin and clip)Shorthand overrides other properties! Even if you set background only for URL, it overrides position and everything else;
img
doesn’t inherit the size of the container by default; if you put img intodiv
and set height to div — image will be still the size it originally is; to change that you have to apply the size directly to the image itself;- When you want to set a width/height of the image to % value, make sure that container is displayed as inline-block;
- Image as an html tag is not a really powerful way to style — there are much less possibilities offered; therefore, often it’s recommended to use div with background images instead; however, it’s less accessible, so it’s aleays good to weigh all benefits;
- If you get an image with the surrounding container, and image is
inline
element — it might show an odd white space (additional padding) at the bottom; if you add vertical-align (any value) to the continer, or turn img to adisplay:block;
it’ll be gone — it’s a bug;
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_images/Using_CSS_gradients
- Linear:
background-image: linear-gradient(to left bottom, red 70%, blue, yellow, green);
- direction:
to top right
, or just30deg
- multiple colors (as many as you want); also
hex
code,rgba()
s etc. linear-gradient(red, transparent);
— to define transparency;
- direction:
- Radial:
background-image: radial-gradient(circle 20px at top left, yellow 50%, red, blue);
- position may be also like
circle at 20% 50%
(from the left and from the top); circle 20px
is a radius of the circle (doesn’t work for ellipse, which is a default property)ellipse 20px 50px
is a width/height of the ellipse; ellipse also is set by default if we don’t specify anything else;ellipse farthest-side
/ closet-side / closest-corner / farthest-corner— the farthest side is touching the edge of the container;
- position may be also like
background: linear-gradient(to top, rgba(80, 68, 18, 0.6) 10%, transparent), url("images/freedom.jpg") left 10% top 20%/cover no-repeat border-box, green;
- Multiple backgrounds result in showing the one on the top (specfied first);
- Only one background-color can be used;
- All BGs should be separated with comma;
https://developer.mozilla.org/en-US/docs/Web/CSS/filter
filter: blur(10px);
- there’s a number of predefined functions
https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorials/SVG_from_scratch/SVG_and_CSS
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Values_and_Units
Can be applied to font-size, padding, border, margin, width, height, top, bottom, left and right properties (for position).
- Absolute units usually ignore user settings (e.g. if they increased font in the browser):
px
,cm
andmm
(not used) - Viewport units: height
vh
, widthvw
,vmin
,vmax
. - Font-relative units:
em
,rem
(rootem
) %
Position property affects how % apply:
- The containing block determines where an element will be positioned from when using properties like
top
,left
,right
, andbottom
. - The box model (padding + content) of the containing block is used for positioning.
- For
absolute
, it matters that the parent hasposition
other thanstatic
to serve as a containing block. sticky
is a bit of a hybrid and depends on scroll context.
Position | Containing Block | Notes | What counts as full size of the container? |
---|---|---|---|
static |
Closest block-level ancestor (usually the parent) | No special positioning. Uses normal document flow. | Ancestor content |
relative |
Same as static – closest block-level ancestor |
Element stays in the flow, but can be nudged using top , left , etc. |
Ancestor content |
absolute |
Closest ancestor that has position: relative , absolute , or fixed (not static) |
If no such ancestor is found, it falls back to the initial containing block (usually <html> or viewport). |
Ancestor content + padding |
fixed |
The viewport (browser window) | Ignores parent hierarchy. Fixed to the screen, regardless of scroll. | Viewport |
sticky |
Nearest scrollable ancestor (or viewport) | Acts like relative until a threshold is crossed, then sticks like fixed . |
Ancestor content / Viewport |
100% height issue
When position static
or relative
is applied to the element, and the element has an ancestor body
(which is block and has some width and height), we can see the width of the element is inherited, but height is still 0. To fix that we have to add: html, body { height: 100%; }
— because these elements don't have an explicit height, then its height is just based on content — and by default, it's not considered 100% of the viewport.
https://developer.mozilla.org/en-US/docs/Web/CSS/font-size
html { font-size: 100%; }
can override default browser font-size- When a font-size applied in px, it overrides browser settings (and font won’t be responsive)
- By default, browser applies sizes in
em
— which is based on base font size (typically 10px); those default browser sizes depend on the elements — paragraph, h1 or h2, or smth else. - The problem with
em
is that those values are inherited from parent containers — this means that element with 1.5em inside another elements with 1.5em will use this value as a basic for calculations, and we get 1.5*1.5=2.25 instead. - On the other hand,
rem
is always based on root font size (browser default).
- Makes it easier to create modal views
- 100vh = 100% of the viewport; it’s just 1% always refers to the viewport
vmin
— takes a value of the smaller viewport side (e.g. if it’s horizontally oriented — that will be height);vmax
— respectively, vice versa.
Hiding Scrollbars on Windows machines
Of course, those are just general notes — there might be different edge cases.
font-size
(root element)—%
or nothingfont-size
—rem
(em
for children only)padding
/margin
—rem
width
/height
—%
,vw
,vh
top
/bottom
/left
/right
—%
border
,shadow
, other effects —px
https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style
https://developer.mozilla.org/en-US/docs/Web/API/Element/classList
var backdrop = document.querySelector('.backdrop');
console.log(backdrop);
console.dir(backdrop); /* shows all children and props in DOM */
querySelector
selects only one element; if I want all objects that have this identifier to be selected, we usequerySelectorAll
- manipulating classes:
className
ormodal.classList.add('');
orremove
Alternative syntax to use to access properties in css: element.style.backgroundImage
is the same as element.style[’background-image’]
- Absolute lengths on W3.org: https://www.w3.org/TR/css-values-3/#absolute-lengths
- More about device sizes: https://bjango.com/articles/min-device-pixel-ratio/
- Media queries theory: https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries
- Applying media queries: https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries
- In inspector tools in browser, at the very left top level there is a selector for display site in a web/mobile device frame — there’s also an option to show/hide device frame
- Modern devices have a high pixel density, therefore we have to decrease pixel measurements of the site to display it correctly → so there is a need to apply pixel ratio for the mobile version to be displayed correctly; this is done by
viewport
- For that we add a line into index.html file:
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes, maximum-scale=2.0, minimum-scale=1.0">
— that makes browser render a view not based on browser physical pixels, but calculated based on a phone we simulate, closer to reality.initial-scale
is a zooming option (by chaging that we can make site look much closer; the only thing is that there’s a chrome bug, and it’s not working right away, one needs to switch between desktop/mobile view one more time to see changes)user-scalable
— allows (or not) users to zoom in/out (is yes by default); same for maximum-scale or minimum-scale (sets a limit for users to zoom in/out)- typically all is needed is:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
The difference between viewport
and media queries
is that the latter allows to change the design depending on the size of the screen; while viewport deals specifically with pixel density problem; with it also the changes are up to a browser, with media queries we define the scope of changes manually.
-
Basic structure of media query (media query is basically like IF condition)
@media (min-width: 40rem) { #product-overview h1 { font-size: 1.6rem; } }
-
Typically you start with mobile (mobile-first). Which means that we start writing from minimal width, and then add rules for wider screens.
-
How it works in code
css CopyEdit /* Basic style for mobile (small screens) */ button { font-size: 14px; } /* For wider screens (tablet, desktop) */ @media (min-width: 768px) { button { font-size: 18px; } }
✅ First, the button is small on phones.
✅ Then, when screen width is 768px or more, the button becomes bigger.
-
-
You can write many media queries in your CSS. They all work together, but the order matters. More specific or later-written rules override earlier ones (because of CSS cascading)
Smaller queries should come first, then bigger ones — from smallest to largest.
-
How it works in code
css CopyEdit @media (min-width: 600px) { button { color: blue; } } @media (min-width: 400px) { button { color: red; } }
If the screen is 500px wide:
- Only the 400px rule applies (red button).
- The 600px rule is skipped (because 500px is smaller than 600px).
-
-
Typically all media queries are added at the end of the code
-
Addressing multiple criteria:
@media (min-width: 40rem) and (min-height: 60rem) { #product-overview { height: 40vh; background-position: 50% 25%; }
- When is that needed? For example, we want to adjust a width of the image when we use a vertical oriented narrow screen; which means, if the height it too small (horizontal orientation), image height should still be visible, and stay 40vh, and only if the height grows bigger, we want to change it;
- Another way to do it is:
@media (min-width: 40rem) and (orientation: portrait)
instead; but we should keep in mind that this doesn’t work good all the time for desktop browsers. - If we want to change AND rule to OR, then we use comma instead of and:
@media (min-width: 40rem), (orientation: portrait)
https://gs.statcounter.com/screen-resolution-stats
https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Forms/Styling_web_forms
https://stackoverflow.com/questions/1895476/how-do-i-style-a-select-dropdown-with-only-css
-
Sometimes to style input, one wraps them into divs together with labels, or one can target labels and inputs individually.
-
By default, labels and inputs are inline (so typically you want turn them into blocks)
-
Advanced attribute selectors
Selector Meaning Example Matches [attr]
Has attribute input[disabled]
<input disabled>
[attr="value"]
Exact match a[target="_blank"]
<a target="_blank">
[attr~="value"]
Has word in list div[class~="active"]
<div class="btn active">
[attr^="value"]
Starts with value img[src^="https://"]
<img src="https://...">
[attr$="value"]
Ends with value a[href$=".pdf"]
<a href="file.pdf">
[attr*="value"]
Contains value input[name*="user"]
<input name="user-name">
`[attr ="value"]` Exact or starts with value + dash `div[lang .signup-form input[id*="terms"] + label
— e.g. this will target all labels that follow directly specific inputs which ID contains word “terms”.signup-form input:not([type="checkbox"])
— targets all the inputs inside this class, which are not checkboxes
-
Fonts for inputs and buttons should be set specifically by
inherit
— otherwise, browser set another default font -
For the focus state there’s
outline
property used — it’s not counting in box size, and goes out of the box; in forms you work a lot with pseudo-classes like:checked
,:focused
etc. -
Disable state for the buttons:
.button[disabled]
button:disabled
Any element with class="button"
anddisabled
attributeOnly real <button>
elementsGood for custom components Good for real buttons Checks attribute manually Checks browser form state -
Checkbox (box with tick itself) — is a special element in a browser; sometimes to override styles one should use
-webkit-appearance: none; -moz-appearance: none; appearance: none;
-
To style invalid states for forms you can use
class=”invalid”
; and then apply in conditionally via JS or after server validation; html and css also have an automatic approach to this with:invalid
pseudo selector:.signup-form :invalid {}
targets all invalid inputs in the class; there’a also a:valid
pseudo selector
- Serif / Sans / Monospace / Cursive / Fantasy — generic families
- Font family — a specific font, e.g. Times, Georgia.
- There are also standard font settings in a browser to display different fonts —e.g. in Chrome it is in
chrome://settings/fonts
(standart font → san-serif → serif → mono) - Custom fonts that are specifically included into a project (like web fonts from the 3rd party like Google fonts, or fonts retrieved from the server where we saved them)
Defaults:
- If nothing is applied at all, then browser will apply default font-family (the way it’s set up on the user’s side);
font-family: sans-serif;
— the browser will select a font, default for the generic font-family setting;font-family: “Montserrat”, "Zapfino", sans-serif;
— the browser will apply Montserrat, if not found out, then Zapfino, then the default family to sans-serif (e.g. Verdana in Chrome).
-
One can import fonts into each html file, like this:
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Tagesschrift&display=swap" rel="stylesheet">
-
Or alternatively, one can add it right into a shared css file (better approach in general):
@import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Tagesschrift&display=swap');
Property | Meaning | Example |
---|---|---|
font-family |
Font face/design | font-family: "Roboto", sans-serif; |
font-size |
Size of the font | font-size: 16px; |
font-weight |
Thickness (boldness) | font-weight: 700; |
font-style |
Italic or normal | font-style: italic; |
font-variant |
Small caps or normal | font-variant: small-caps; |
line-height |
Height between lines | line-height: 1.5; |
letter-spacing |
Space between letters | letter-spacing: 0.05em; |
white-space |
Changes the way white spaces are displayed in text | white-space: pre; |
word-spacing |
Space between words | word-spacing: 0.2em; |
text-align |
Horizontal text alignment | text-align: center; |
text-decoration |
Underline, overline, line-through | text-decoration: underline; |
text-transform |
Change letters to uppercase, lowercase | text-transform: uppercase; |
color |
Text color | color: #333; |
That can be used to import your own font into the website; @font-face
in css registers a new font with your custom name and all properties:
/* Regular */
@font-face {
font-family: 'MyFont';
src: url('/fonts/MyFont-Regular.woff2') format('woff2');
font-weight: 400;
font-style: normal;
}
/* Bold */
@font-face {
font-family: 'MyFont';
src: url('/fonts/MyFont-Bold.woff2') format('woff2');
font-weight: 700;
font-style: normal;
}
/* Italic */
@font-face {
font-family: 'MyFont';
src: url('/fonts/MyFont-Italic.woff2') format('woff2');
font-weight: 400;
font-style: italic;
}
For that to work, we’ll need to import all the font files responsible for each font weight and style and specify for each of them properties they have.
-
TTF/OTF are more common and supported
-
WOFF format is more compressed and better browser import, but the browser support is more limited
https://developer.mozilla.org/en-US/docs/Web/CSS/text-decoration
https://developer.mozilla.org/en-US/docs/Web/CSS/text-shadow
https://developer.mozilla.org/en-US/docs/Web/CSS/font
The order of the values is important! Some of them are optional (but font size and family are required).
font: italic small-caps 700 1.2rem/2 "Montserrat", sans-serif;
- 1.2rem /2 ( after slash there is a line-height)
font: menu;
this value refers to the same font that is applied to different parts of the operating system, e.g. how menus look by default (might be alsostatus-bar
e.g.)
font-display
controls how the font loads and displays when it’s not immediately available (e.g., on slow networks).
auto |
Browser decides (default behavior) |
---|---|
block |
Wait briefly for font → show it when ready (text invisible for a short time) |
swap |
Show fallback font immediately → swap to custom font when ready |
fallback |
Show fallback font immediately → swap only if ready quickly |
optional |
Like fallback, but browser may skip font download entirely if it's slow or low priority |
https://developer.mozilla.org/en-US/docs/Glossary/Flex
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_flexible_box_layout/Basic_concepts_of_flexbox
https://caniuse.com/?search=flexbox
css-flexbox.pdf
-
display: flex;
orinline-flex
turn an element into flex container that has flex-items inside; after that we can apply different properties for both parent and childProperty Layout Starts new line? Common use flex
Block-level ✅ Yes Layout rows, sidebars inline-flex
Inline-level ❌ No Buttons, badges, icons inside text -
By default flex-container has
flex-direction: row;
andflex-wrap: nowrap;
declarations being automatically applied -
Flex Container Properties
(You set these on the parent element — the flex container)
Property What it does display: flex
Turns the element into a flex container flex-direction
Direction of items ( row
← default,column
, etc.)flex-wrap
Allows items to wrap onto multiple lines justify-content
Horizontal alignment (main axis) align-items
Vertical alignment (cross axis) align-content
Space between rows in wrapped content; gap
Adds spacing between items (no need for margins) -
flex-flow: row wrap;
(shorthand)
You set these on the child elements inside a flex container. And the properties are applied to each item individually:
Property | What it does |
---|---|
flex-grow |
How much it expands to fill space (0 by default); defines how the excessive space inside the container is distributed between the items |
flex-shrink |
How much it shrinks when space is tight (1 by default); same principle as for flex-grow |
flex-basis |
Initial size before growing/shrinking; defines size of the item by the main axis; by default is set to auto; can be defined also in %; |
But if we have a flex container AND flex-direction is set to row, flex-basis will override width property (same with height, but with column value); |
|
flex |
Shorthand for flex-grow , flex-shrink , flex-basis . |
E.g. flex: 0 1 auto; (all default settings) |
|
align-self |
Overrides align-items for this item only |
order |
Controls visual order of the item (0 by default) |
Adding the z-index
to an element only has an effect, if the position
property with a value different from static
was applied to this element. One exception from this behaviour is flexbox: Applying the z-index
to flex-items (so the elements inside of the flex-container) will change the order of these items even if no position
property was applied.
css-grid.pdf
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout
https://css-tricks.com/snippets/css/complete-guide-grid/
CSS Grid is a two-dimensional layout system that allows you to place items into rows and columns. (Chrome has quite good developer tools to support grid development — highlights grid elements and borders).
Grid and document flow
- Elements with
position: absolute
orposition: fixed
are removed from the normal document flow, including both Flexbox and Grid layouts. - They do not participate in the layout or sizing decisions of the CSS Grid or Flex container they may be visually inside of.
Think of it like this: they’re "on top" of the layout, not "inside" it structurally.
But be careful:
- Even though they're out of the flow, if a positioned element is nested inside a grid container, it may still use that container as a positioning context if the container is
position: relative
or any non-static
value. - Also, CSS Grid allows positioning absolutely positioned children relative to grid areas, using properties like
grid-area
, which is a unique feature.
These are applied to the grid parent element (the container):
Property | Description |
---|---|
display: grid |
Turns the element into a grid container. |
grid-template-columns |
Defines the number and size of columns (e.g., 1fr 2fr or repeat(3, 1fr) — shorthand for 1fr 1fr 1fr ; |
grid-template-rows |
Defines the number and size of rows (e.g., 100px auto 1fr ). |
gap or grid-gap |
Sets space between rows and columns (e.g., 10px , or 10px 20px ). |
justify-items |
Aligns items horizontally in their cells (start , end , center , stretch ). |
align-items |
Aligns items vertically in their cells. |
justify-content |
Aligns the whole grid horizontally when there's extra space. |
align-content |
Aligns the whole grid vertically when there's extra space. |
grid-template-areas |
Defines named areas to place items into using grid-area . |
grid-auto-flow |
Controls automatic item placement (row or column ). |
grid-auto-columns / grid-auto-rows |
Defines size for implicit columns or rows created on the fly. |
You can also set your entire page to be grid, by adding body {display:grid;}
. This is quite common practice.
fr
= fraction of remaining space (e.g.,2fr
gets twice as much as1fr
)- Use
minmax()
for responsive sizing: e.g.,minmax(150px, 1fr)
; it means that column or row it’s applied to, will stretch between those 2 values; auto
is a value that allows grid to take the space that content inside has (depends on whether it’s set to rows or columns);- By default, all items behave like
stretch
— which means, they take all the space available inside the cell; if you start changing properties likejustify-items
oralign-items
, they start being positioned on one of the sides or in the middle, and take only the space needed for the content.
These are applied to the children of the grid (grid items):
Property | Description |
---|---|
grid-column-start / grid-column-end |
Specify which columns an item starts/ends in. |
grid-row-start / grid-row-end |
Specify rows an item starts/ends in. |
grid-column |
Shorthand to define start and end column through slash (grid-column: 1 / 3 ). |
grid-row |
Shorthand for start and end row. |
grid-area |
— Can refer to named area like grid-area: header; |
— Or be used as shorthand for all four start/end properties; grid-row-start / grid-column-start / grid-row-end / grid-column-end |
|
justify-self |
Horizontal alignment per item inside its cell. |
align-self |
Vertical alignment per item inside its cell. |
place-self |
Shorthand for align-self + justify-self . |
-
We can say that we want certain cell to occupy certain number of columns / rows
/*way 1*/ grid-column-start: 2; grid-column-end: 4; /*way 2*/ grid-column-start: 2; grid-column-end: span 2; /*ends after occupying 2 cells*/
- If there’s no space to occupy anymore, increasing span 2 to span 10 won’t change anything; default value for each cell
span 1
- you can also define negative span number (e.g.
grid-column-end: span -1;
will mean 1 column from the right) - We can even reach an overlap between two cells if we explicitely define that they should start and end on the same rows and the same columns — automatically grid property tries to avoid this; but if it happens, the element (cell) that is written last according to DOM, will be on top;
- If there’s no space to occupy anymore, increasing span 2 to span 10 won’t change anything; default value for each cell
-
grid-template-columns: fit-content(200px);
-
The column will shrink to fit the content, but never grow wider than
200px
. -
If the content is narrower than 200px, the column shrinks to fit it.
-
If the content is wider than 200px, the column becomes 200px max and content overflows or wraps.
-
Essentially, it’s the same as
minmax(auto, max-width);
-
Example:
.grid { display: grid; grid-template-columns: 100px fit-content(300px) 1fr; }
This creates: a fixed 100px column + column that grows to fit content, up to 300px max + flexible column that takes the remaining space
-
-
What can be named: grid lines / grid areas (separately with
grid-template-areas
, but that's a different feature)- You can assign multiple names to one cell
-
Naming grid lines: you name grid lines inside square brackets
[]
when defining columns or rows:css CopyEdit grid-template-columns: [start] 1fr [middle] 1fr [end];
This defines:
- A column from
start
tomiddle
(1fr wide) - Another from
middle
toend
(1fr wide)
- A column from
-
With
repeat()
: you can name grid lines insiderepeat()
too:css CopyEdit grid-template-columns: repeat(3, [col-start] 1fr [col-end]);
This creates: 3 columns, each with named lines:
col-start
andcol-end
-
How to use named lines: later, you can position items like this:
css CopyEdit .item { grid-column: col-start 2 / col-end 3; }
This means: start at the 2nd occurrence of a line named
col-start
/ end at the 3rd occurrence of a line namedcol-end
. This is useful when you have repeating names — it counts how many times the name appeared so far. -
Template for the grid
grid-template-areas: “header header header header" "side side main main" "footer footer footer footer”;
- If you don’t want to give a name to certain cell, you put
.
instead - After defining such a structure of the grid inside the grid container, you can later assign for each element a property like
grid-area: main;
and it will behave the way it’s specified in the template; it will even override DOM order;
- If you don’t want to give a name to certain cell, you put
grid-row-gap
,grid-column-gap
grid-gap: 10px 30px;
(rows, columns)grid-gap: 10px;
(is taken for both rows and columns)
Explicit grid — the grid structure you define yourself using grid-template-columns
or grid-template-rows
. Implicit grid — tracks rows or columns that the browser automatically creates because:
- You placed more items than the grid can hold
- You explicitly positioned an item outside the defined grid
These tracks are controlled with: grid-auto-rows
or grid-auto-columns
.
Property | Controls... | Default |
---|---|---|
grid-auto-flow |
Placement direction | row |
grid-auto-rows |
Height of implicit rows | auto |
grid-auto-columns |
Width of implicit columns | auto |
-
grid-auto-flow
Controls how the browser places items in the grid when you don't explicitly place them.
row
(default) → Fills rows left to right, then moves to the next row.column
→ Fills columns top to bottom, then moves to the next column.dense
(can be added to either) → Tries to fill holes in the grid if items fit.
css CopyEdit .container { display: grid; grid-template-columns: repeat(3, 100px); grid-auto-flow: column; }
This will place items top to bottom in columns, not left to right.
-
grid-auto-rows
&grid-auto-columns
Define the size of implicitly created tracks (rows or columns) when you don’t define all rows/columns, but items force the grid to create more.
-
grid-auto-rows
example:css CopyEdit .container { display: grid; grid-template-columns: 100px 100px; grid-auto-rows: 80px; }
If items overflow and need new rows, those new rows will each be 80px high.
-
grid-auto-columns
example:css CopyEdit .container { display: grid; grid-template-rows: 100px 100px; grid-auto-columns: 60px; grid-auto-flow: column; }
This will fill items in columns, and any new columns will be 60px wide.
-
-
auto-fill
vsauto-fit
.Both are used in:
grid-template-columns: repeat(auto-fill/auto-fit, minmax(150px, 1fr));
(only in columns!)Keyword What it does Result auto-fill
Creates as many columns as fit, keeps empty slots Layout stays the same, even if items are missing. auto-fit
Creates as many columns as fit, collapses empty ones Items stretch to fill all space.
The difference between grid and flexbox typically boils down to the fact that 1st is 2-dimensional, and the 2nd — 1-dimentional.
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_transforms/Using_CSS_transforms
https://3dtransforms.desandro.com/
Function | Description |
---|---|
translateX(x) |
Moves the element left/right. |
translateY(y) |
Moves the element up/down. |
translate(x, y) |
Moves an element along the X and Y axes. |
translateZ(z) |
Moves in 3D space (needs transform-style: preserve-3d ). |
translate3d(x, y, z) |
Moves in all three dimensions. |
scaleX(x) |
Scales only the width. |
scaleY(y) |
Scales only the height. |
scale(x, y) |
Scales the element by x horizontally and y vertically. |
scaleZ(z) |
Scales in 3D (rarely used alone). |
scale3d(x, y, z) |
Scales in all three dimensions. |
rotate(angle) |
Rotates around the Z-axis (2D). |
rotateX(angle) |
Rotates around the X-axis (3D flip forward/back). |
rotateY(angle) |
Rotates around the Y-axis (3D flip side-to-side). |
rotateZ(angle) |
Same as rotate , explicitly Z-axis. |
rotate3d(x, y, z, angle) |
Rotates around a vector in 3D space. |
skew(x, y) |
Skews the element in X and Y directions. |
skewX(angle) |
Skews only horizontally. |
skewY(angle) |
Skews only vertically. |
perspective(n) |
Applies a 3D perspective (used with 3D transforms). |
matrix(a, b, c, d, e, f) |
A combination of 2D transforms (rarely used directly). |
matrix3d(...) |
Advanced 3D transform matrix (16 values, rarely written manually). |
none |
Resets any transformation. |
- You can combine multiple transforms like so:
transform: translateX(50px) scale(1.2) rotate(30deg);
translate
property is to move elements by axes; if the element is rotated, those axes rotate together with it;- You can “cancel” the transformation for the image inside container: if container has skew(20deg) applied, we can use skew(-20deg) to get image straight again.
- The
transform-style
property in CSS controls whether an element's children are rendered in 3D space or are flattened into the 2D plane of the parent. Can beflat
orpreserve-3d
;- If you apply 3D transforms to elements inside a container with
transform-style: flat
, and then transform the container itself, the inner elements will appear as if they are flattened onto the container’s plane — their 3D positioning won’t be preserved.
- If you apply 3D transforms to elements inside a container with
transform: perspective;
— adds 3D depth to child elements by setting distance from the viewer; you can apply pixels (which mean the distance between viewer and the object);- there’s also a
prospective: 1000px;
property — but that should be applied to the parent object; in this case all children of it will have the same prospective.
- there’s also a
transform-origin: top left;
(or 50% 50%) allows to set the point relative to whch the transformation will be done; same forperspective-origin
backface-visibility
hides or shows the back of an element when rotated (visible
orhidden
); i.e. completely hides the back of the element.will-change
hints the browser to optimize performance for transforms (e.g.,will-change: transform;
).
Transition — is a change from 1 state to another. We can also add animation to transition.
css-16-animations-summary.pdf
https://developer.mozilla.org/en-US/docs/Web/CSS/transition
transition: all;
or transition: opacity 0.5s, transform 500ms;
This code interpolates the change of selected value (e.g. opacity or transformation of the object).
Property | Purpose | How It Works | Key Syntax |
---|---|---|---|
transform |
Visually modifies the element (rotate, scale, move, skew) | Instantly applies a visual effect without animation | transform: scale(1.2) |
transition |
Animates changes between states smoothly | Applies animation between two style states (e.g., default → hover) | transition: transform 0.3s ease |
animation |
Creates complex, multi-step animations | Uses @keyframes to define steps; supports loops, direction, delays, etc. |
animation: slide 2s infinite |
-
transition: opacity 200ms 1s ease-out;
Can be translated to: "Animate any changes in the
opacity
property (for the element to which thetransition
property is applied) over a duration of 200ms. Start fast and end slow, also make sure to wait 1s before you start".transition-delay: 1s;
also can delay a transition -
Easing function might be even created right in a Chrome developer tools
-
To make elements "invisible" (like using
display: none;
), but still have them animating through opacity, you should usevisibility: hidden;
combined withopacity: 0;
.Unlike
display: none
,visibility: hidden
keeps the element in the document flow (i.e., it still takes up space) and allows for opacity transitions.Here’s an example:
css CopyEdit .element { opacity: 0; /* Initially hidden */ visibility: hidden; /* Keeps the element in the flow, but invisible */ transition: opacity 1s ease, visibility 0s 1s; /* Allows opacity transition */ } .element.show { opacity: 1; /* Makes it visible */ visibility: visible; /* Ensures the element is part of the flow */ transition: opacity 1s ease, visibility 0s; /* Timing for visibility change */ }
Explanation:
- The element starts off with
opacity: 0
andvisibility: hidden
. - When you add the
.show
class, the element fades in (opacity: 1
) and becomes part of the flow (visibility: visible
). - The
transition
property animates the opacity, andvisibility
will change after the opacity transition ends, so it can be used for smooth opacity-based animations.
This setup allows you to animate an element’s opacity and visibility while keeping it part of the document flow for the animation.
- The element starts off with
https://developer.mozilla.org/en-US/docs/Web/CSS/animation
-
Any element that later receives this
wiggle
animation, will use it by animation:wiggle 200ms 3s 8;
:@keyframes wiggle { from { transform: rotateZ(0); } to { transform: rotateZ(10deg); } }
@keyframes wiggle { 0% { transform: rotateZ(0); } 50% { transform: rotateZ(-10deg); } 100% { transform: rotateZ(10deg); } }
-
animation: wiggle 200ms 1s ease-out 8 alternate forwards running;
Can be translated to: "Play the wiggle keyframe set (animation) over a duration of 200ms. Between two keyframes start fast and end slow, also make sure to wait 1s before you start. Play 8 animations and alternate after each animation. Once you're done, keep the final value applied to the element. Oh, and you should be playing the animation - not pausing."
-
There are a lot of additional properties like
animation-delay
,animation-direction
,animation-iteration-count
etc.
var ctaButton = document.querySelector(”.main__nav-btn-cta”);
ctaButton.addEventListener('animationstart', fucntion() {
console.log('Animation started');
});
There are also animationend
and animationiteration
events.
In animation event in the developer tools in the browser there’s detailed information about each event — transformations, timing etc.
css-17-future-css.pdf
https://www.w3.org/TR/#tr_Cascading_Style_Sheets__CSS__Working_Group
-
Variables
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_cascading_variables/Using_CSS_custom_properties
When using a variable, you can add a fallback color in case the variable won’t be defined for some reasor (e.g. external package); but it won’t work for the browsers that don’t support variables anyway;
:root { --dark-green: #0e4f1f; --highlight-color: #ff1b68; } a { color: var(--highlight-color, #0e4f1f); }
-
Prefixes are used when browsers just start to support some feature early; in case the final version is different from what they have added with prefix, the old code won’t be broken with the new syntax. All browsers have different prefixes. In order to add all the right prefixes — you can do it with automatic tool.
https://developer.mozilla.org/en-US/docs/Glossary/Vendor_Prefix
div { display: -webkit-flex; /*if browser understands only this*/ display: -ms-flexbox; display: flex; /*if browser understands typical flex*/ }
-
@supports query checks if a certain property is supported by browser, and only executes code if browser supports it. You can also use more complex queries with conditions.
https://developer.mozilla.org/en-US/docs/Web/CSS/%40supports
@supports (display: grid) { contaner { display: grid; } } contaner { display: inline; }
-
Polyfills are JS packages that enable certain CSS features in browsers which wouldn’t support is otherwise. They apply a feature with other CSS tricks by using Java Script, which however influences performance.
https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-Browser-Polyfills
-
Reset-libraries (e.g. normalize.css) help to keep all the default styles across browsers to make them all consistent. You will need to import them first in your code. But then users have to also download all that initial code (performance).
-
Vanilla CSS is when you write the whole code on your own; there are also many packages with ready-to-use styling (classes) like Bootstrap or Tilewind; again it gives a lot of advantages, but you need to rely on package and users have to download them first, also you have not a lot of control. Utility frameworks only offer some classes, while component frameworks offer whole components.
CSS is case-insensitive. Which means that snakeCase
won’t be understood in case it matches snakecase
.
-
Kebab style
E.g.,
background-color
,font-size
is used in CSS because:- Consistency with CSS Syntax – CSS properties are written in kebab-case by default (e.g.,
font-size
,margin-left
). Keeping class names in kebab case makes everything look uniform. - Better Readability –
main-header-title
is easier to read thanmainHeaderTitle
(camelCase) orMain_Header_Title
(snake_case). - Compatibility with HTML – HTML class names are case-insensitive, so using camelCase (
mainHeaderTitle
) can lead to unexpected behavior in some environments. - Avoids Issues in Selectors – Some CSS preprocessors (like Sass) and tools may not handle camelCase properly, while kebab-case works reliably everywhere.
- Consistency with CSS Syntax – CSS properties are written in kebab-case by default (e.g.,
-
BEM naming for CSS classes
https://getbem.com/introduction/
BEM stands for Block, Element, Modifier. It is a naming convention for CSS classes that makes styles structured, reusable, and easy to maintain.
- Block (
.block
) – A reusable standalone component (e.g., a button, a navbar). - Element (
.block__element
) – A part of a block that has no meaning on its own (e.g., a button’s icon). - Modifier (
.block--modifier
or.block__element--modifier
) – A variation of a block or element (e.g., a large button).
Why use BEM?
- Avoids conflicts – Each component has its own unique class names. Without BEM, you might have generic class names like
.title
,.container
, or.button
, which can easily collide with other styles in a large project. - Scalable – Works well for large projects. Instead of deeply nested styles, BEM keeps selectors flat and modular. Changing one part of a component won’t break other styles.
- Readable – Other developers can quickly understand your CSS.
- Easier maintenance – Clear structure makes future changes simpler.
- Works Well with JavaScript & React. BEM class names are easy to select in JavaScript (e.g., for event listeners). In React, BEM works great with components and styling frameworks like CSS Modules or SCSS.
<button class="button button--primary"> <span class="button__icon">🎨</span> <span class="button__text">Click me</span> </button>
<nav class="main-nav"> <ul class="main-nav__items"> <li class="main-nav__item"></li> </ul> </nav>
- Block (
https://sass-lang.com/install/
-
Sass and SCSS are both syntaxes of Sass (Syntactically Awesome Stylesheets):
Syntax Description File Extension Sass Indented syntax, no curly braces {}
or semicolons;
. Cleaner but less like CSS..sass
SCSS CSS-like syntax with curly braces {}
and semicolons;
. Easier transition from CSS..scss
SCSS is more popular because it's closer to standard CSS.
-
This language is not understood by the browser by default. You let some tool compile your code into CSS.
-
To do that, after writing your code in e.g.
file.scss
, you need to open a terminal (better in VS code, right in the folder where you’re working) and write the following:$ sass file.scss main.css
This will create (or replace) file
main.css
using yourfile.scss
as a source. -
However, if you want to watch the changes automatically:
$ sass --watch file.scss:file.css
This will change file
main.css
using yourfile.scss
as a source on the go, in real time.
-
-
Nested rules (nest properties like flex or font (that start with the same font-) into one curly brackets:
font {size: 30px; weight: 400;}
-
Variables:
$main-color: #234567;
and then use everywhere$main-color
. You can also store a list of values in a variable, e.g.1px solid red
— basically anything that is used at the right side of the property (value) in CSS. You can also create maps:$colors: (main: #456738, secondary: #fa1234); div { color: $solid map-get($colors, main); }
You can also use variable inside a variable.
-
Helper functions: e.g. map-get to extract variables from the mapping (above); https://sass-lang.com/documentation/modules/; there are functions like lighten the color for example.
-
Calculations.
padding: $size-default * 3 0;
It supports basic operations -
Media-queries might be also nested inside the element, instead of collecting all queries at the end of the file.
-
Inheritance. When inside one class you inherit properties from the other classes (then we’ll have the same set of the rules of the class you inherit):
.class1 { } .class2 { @extend .class1; }
-
Mixin. It’s a way to reuse groups of CSS styles throughout your project. Instead of repeating code, you define it once in a mixin and use it wherever you need.
// Define a mixin with the name "button-styles" @mixin button-styles($color, $padding: 10px) { background-color: $color; padding: $padding; border-radius: 5px; color: white; } .button-primary { @include button-styles(blue, 15px); }
@content
is a special directive in Sass that allows you to pass a block of styles into a mixin. It’s useful when you want to inject custom styles inside a predefined structure.-
The code in scss:
// Mixin definition with @content @mixin card { border: 1px solid #ccc; padding: 15px; border-radius: 5px; // Here, @content will inject custom styles @content; } // Using the mixin .card-primary { @include card { background-color: blue; color: white; } } .card-secondary { @include card { background-color: green; color: black; } }
-
Results in:
.card-primary { border: 1px solid #ccc; padding: 15px; border-radius: 5px; background-color: blue; color: white; } .card-secondary { border: 1px solid #ccc; padding: 15px; border-radius: 5px; background-color: green; color: black; }
-
-
Ampersand operator. instead of writing all the time
.class:hover, .class:active {}
separately from the main styles for that class, we can nest inside this class definition&:hover, &:active {}
Partials in SCSS allow you to break your CSS into smaller, reusable pieces. Instead of having one big stylesheet, you can organize your styles logically (e.g., _buttons.scss
, _header.scss
). This makes it easier to manage, maintain, and update.
-
The underscore tells Sass not to generate a separate CSS file for it.
-
Import the partial in your main SCSS file:
scss CopyEdit // main.scss @import 'buttons'; @import 'header';
-
No need to include the underscore or the
.scss
extension—just the filename.
Result: all styles from _buttons.scss
and _header.scss
are merged into your final compiled CSS file, even though there are no imports in it at all, all the import happens on scss side.