- INDEX
- Cascading Style Sheets (CSS)
- How CSS works
- Selectors
- Box Model
- Colors
- Font & Text
- Units
- Shadow
- images
- Calculations Built in Functions
- data attributes
- Table
- Form
- Modules & Layers (multiple style sheets)
- Browser Support (CSS Vendor Prefixes)
- Notes & Tricks
- Tricks and Techniques
- Display vs Visibility
- How to center an element
- Scrolling
- Customizable font-size calculation
- Custom Audio Player
- Double Border Corners
- Rotating elements (3D perspective)
- Hamburger menu that opens navbar from the right as a growing circle
- Floating Cursor Animation
- Chat Messages
- Facebook-style layout
- Spacer Component Trick
- Scrollable part of a container
- Tracking the scrollbar width
- Filler Technique
- Book Design
- Grid shapes
- Full Bleed Layout
- Specialty story grid (news website)
- General Notes
- Questions
- Tricks and Techniques
CSS is a language that describes the visual style and layout of the content (HTML) in a web page.
-
CSS stands the phrase Cascading Style Sheets.
-
"Cascading" Process of combining different stylesheets and resolving conflicts between different CSS rules and declarations by the browser, when more than one rule applies to a certain element.
- means that a lower priority style can be overridden by a higher priority style -> Specificity
- also the styles are applied in a specific order, so the last style will be applied (chronological order)
Great article about it here
-
"Style" is a set of rules that define how the content of an element should be displayed.
-
"Sheets" means that the rules are organized into groups called stylesheets.
-
-
CSS Rule: It consists of
properties
andvalues
that are applied to HTML elements (selectors
) to style them.
-
You don't quite start with a blank canvas; HTML tags do include a few minimal styles. For example, here are the built-in styles for
<a>
tags, in Chrome 86:a { color: -webkit-link; cursor: pointer; text-decoration: underline; }
- These styles are part of the user-agent browser stylesheet that the browser applies to all web pages by default (each browser has its own default styles).
-
But we can override these styles and add our own styles using CSS. and there are 3 ways to add styles to an HTML element:
-
Inline style
-
it's a style that is applied directly to the element using the
style
attribute.<h1 style="color:red;"></h1>
-
It's a bad practice because:
- it mixes the content with the style, and it's hard to maintain and update.
- it only applies to the specific element, and it's not reusable for other elements.
-
-
Internal style
-
External style
-
it's a style that is applied to the whole page using an external CSS file.
<Link>
tag is not a url link, it's a used for linking the HTML file to the CSS file.rel
attribute is used to specify the relationship between the HTML file and the linked file. if we didn't specify it, the browser will assume it's a stylesheet file.
-
-
-
Note:
CSS Reset is a set of styles that are have 2 primary jobs: Remove any strange default styles that browsers apply to HTML elements to make sure that the code runs the same on all browsers, and Flatten the CSS to make it easier to style by removing any default margins, paddings, and other styles that can make it difficult to style.
-
References to use:
-
John Smilga CSS Default Starter / Global Styles / Tools
- read the README.md file for instructions
-
-
Notes:
-
When writing normalize css code, use
body
for the properties that are inherited by all elements (likefont
related properties), and use*
for the properties that are not inherited by all elements (likebox-sizing
)./* Global Styles */ html { box-sizing: border-box; font-size: 10px; } *, *::before, *::after { box-sizing: inherit; margin: 0; padding: 0; } body { font-family: 'Arial', sans-serif; font-size: 1.6rem; line-height: 1.6; }
-
-
The browser receives the HTML file and starts to parse it
- When parsing the HTML file, it encounters a
<link>
tag with arel="stylesheet"
attribute, so it starts to download the CSS file - The browser continues parsing the HTML file and builds the DOM tree, in parallel with loading the CSS file.
- When parsing the HTML file, it encounters a
-
Loading CSS file
-
Rendering the page
- The
CSSOM
andDOM
trees are combined into a render tree. - The render tree is used to compute the layout of each visible element and displays them on the screen.
- The browser paints the pixels on the screen -> Visual formatting model
- The
It's the process (Algorithm) of turning the render tree into the actual pixels on the screen after calculating the layout of each element.
-
CSS builds its sense of direction based on this system. It has a block direction (vertical), and an inline direction (horizontal).
- Block direction is like lego blocks: they stack together one on top of the other.
- Inline direction is like people standing in-line; they stand side by side, not one on top of the other.
Because not all languages are left-to-right, top-to-bottom, the browser by default uses block (vertical) and inline (horizontal) properties like
margin-block-start
instead ofmargin-top
These alternatives are known as logical properties.
You can learn more about different writing modes in this wonderful article by Jen Simmons.
-
It uses an algorithm called Box Model to determine the size and position of each box to be painted on the screen.
-
To do that it takes into account factors like:
- dimensions of the box ->
box-sizing
- box type ->
inline
,block
,inline-block
- positioning scheme ->
float
, positioning (absolute
,relative
) - stacking context ->
z-index
- viewport size, dimensions of images, etc.
- dimensions of the box ->
Inheritance is a key concept in CSS that allows styles to be passed from parent elements to their children (descendants).
-
Inheritance is only applied if no style is defined for the element itself.
-
Not all properties are inherited, only some properties that inherit are typography-related (like
color
,font-family
,font-size
, other text properties). -
Example:
body { color: #444444; } p { /* p will inherit the color from the body */ }
-
You can think of it like JavaScript's prototype chain where the child element inherits the properties from the parent element.
class Main { color = 'black'; } class Paragraph extends Main { backgroundColor = 'red'; color = 'blue'; } class Span extends Paragraph {} const s = new Span(); console.log(s.color); // blue
<main style="color: black;"> <p style="color: blue;"> Hello <span>World</span> <!-- will inherit the color from the p --> </p> </main>
-
- "cascaded value" means that there was a style applied to the element itself
-
Notes:
-
styles applied to the
body
element will be inherited by all other elements in the document. if you want to select all elements, (without inheritance), you can use the*
selector.- It has the lowest specificity, so it's easy to override and like a fallback or when overriding default styles.
-
In order to force inheritance, you can use the
inherit
keyword to inherit the value from the parent element (Not recommended)p { border: inherit; }
-
Selectors are used to target the HTML elements that we want to style.
-
There are many types of selectors in CSS, and they can be combined to create complex rules.
-
Based on the type:
- element ->
p
,div
,.. - class ->
.class
- id ->
#id
- attribute ->
[attribute]
- universal ->
*
- It's usually used to reset the default styles of the browser
- element ->
-
Based on the relationship:
- descendant ->
div p
- child ->
div > p
- adjacent sibling ->
div + p
- general sibling ->
div ~ p
- descendant ->
-
Based on the state or position:
:hover
,:active
,:focus
,:visited
,:checked
,:disabled
,:enabled
,:required
,:optional
,:valid
,:invalid
,:in-range
,:out-of-range
,:placeholder-shown
:first-child
,:last-child
,:nth-child(n)
,:nth-last-child(n)
,:nth-of-type(n)
,:only-child
,:only-of-type
-
Based on the content (pseudo-elements):
:empty
,:not(selector)
,:has()
,:where()
,:is()
-
Combinators are used to combine multiple selectors into a single rule. They are used to target elements based on their relationship with other elements.
-
Compound selector
-
selects elements based on multiple classes or selectors
/* Selects all elements with both name1 and name2 set within its class attribute */ .name1.name2 { background-color: yellow; } /* Selects all <p> elements with class="intro", so it's called a class selector */ p.intro { background-color: yellow; }
-
-
Descendant selector
-
selects all elements that are descendants of a specified element (no matter how deep they are nested in the DOM tree)
/* Selects all <p> elements inside <div> elements (parent) even if there are other elements nested between them */ div p { color: red; }
-
-
Child selector (Direct descendant selector)
-
selects all elements that are the direct children of a specified element
/* Selects all <p> elements that are direct children of <div> elements */ div > p { color: red; }
-
-
-
selects an element that is directly after another specific element (on the same level)
/* Selects the first <p> element that is placed immediately after <div> elements */ div + p { color: red; }
-
-
-
selects all elements that are siblings of a specified element (on the same level, but not necessarily directly after it)
/* Selects every <ul> element that is preceded by a <p> element */ p ~ ul { color: red; }
-
It selects elements with a specific attribute (whatever its value or a specific attribute value).
-
[]
selector -> Matches a specific attribute (whatever its value)-
ex:
p[class]
input[type='email'] { background-color: yellow; }
-
-
[=]
selector -> Matches a specific attribute with a specific value- ex:
p[class="dog"]
- ex:
-
[~=]
selector -> Matches a specific attribute whose value appears in a space-separated list of words- ex:
p[class~="dog"]
- ex:
-
[*=]
selector -> Matches a specific attribute whose value contains a specific substring (anywhere in the value)-
ex:
p[attr*"do"]
a[href*='wiki'] { color: red; }
-
-
The
[attribute^=value]
selector matches every element whose attribute value begins with a specified value.div[class^='test-'] { background: #ffff00; } a[href^='https'] { color: red; } /* this will match all links that start with # (anchor links) */ a[href^='#'] { color: green; } a[href^='https']::before { content: '🔒'; }
-
[$=]
selector -> Matches a specific attribute whose value ends with a specific stringa[href$='.org'] { color: red; }
:not(p)
=> Selects every element that is not a<p>
element:nth-child(n)
=> nthmaster- owl selector
* + *
They are used to style certain parts of an element or to style an element in a certain state, position, or relationship.
Pseudo classes are used to apply styles to an element based on its current state or position.
-
Some pseudo classes used with
<a>
,<button>
elements:a:link
=><a>
that are unvisited links with ahref
attributea:visited
=><a>
that has been clicked ona:hover
=><a>
when we are hoveringa:active
=><a>
when we are clickinga:focus
=><a>
when we are tab focusing
-
pseudo classes for Form elements
:checked
-> selects every checked<input type="checkbox">
element:disabled
:enabled
:focus
:required
:optional
:valid
:invalid
:in-range
:out-of-range
:placeholder-shown
-> selects input elements with a placeholder text (when no value is entered yet)
-
pseudo classes for Child elements & Indexes (where the element is in the DOM tree)
-
:root
-> selects the root element of the document-
used to select the root element of the document (the
html
element) -
rem
values will depend on values here -
used for: general styles & css variables
-
It's a pseudo-class, so it has more specificity than the
html
element selector -
It includes all elements and pseudo elements in the DOM unlike
*
selector
-
-
:first-child
-> for the first child of an element (not the first of a type) -
:last-child
-> for the last child of an element -
:nth-child(n)
-> for everyn
th child of an element -
:nth-last-child(n)
-> for everyn
th child of an element, counting from the last child -
:first-of-type
-> for the first child of a type -
:last-of-type
-> for the last child of a type -
:nth-of-type(n)
-> for everyn
th child of a type -
:only-child
-> for the only child of an element -
:only-of-type
-> for the only child of a type
-
-
pseudo class that handles different states of an element -> they are used to style multiple elements that are in a certain state
-
Examples:
:not()
-> selects every element that is not a certain selector:empty
-> selects every element that has no children:target
-> selects the current active target element:has()
-> selects elements that have a specific descendant:where()
-> selects elements that match a list of selectors:is()
-> selects elements that match one of the selectors:selection
-> selects the portion of an (element / text) that is selected by a user
-
They can be combined with other selectors to create complex rules
/* selects every h2 element inside a div or a section */ div h2, section h2 { color: red; } /* the same as the above using :is() */ :is(div, section) h2 { color: red; }
-
They're like pseudo classes but they don't target a specific state, Instead, they target a specific part of an element (sub-elements), ex: placeholder
, first-letter
, ...
They're called "pseudo elements" because they select and style elements that are not part of the DOM (haven't explicitly been created with HTML tags).
-
They create element and insert it
before
/after
content of an element without inserting it in the HTML.-
You can create the same effect by creating a new element and styling it, but it's not recommended because it's not semantic and it's not accessible.
<div class="box">Hello</div> <style> .box::before { content: '🔥'; }
<!-- Also works! ✅ --> <span class="pseudo-element"></span> <div class="box">Hello</div> <style> .pseudo-element { content: '🔥'; }
-
-
Note that they're not part of the DOM, so:
- they can't be selected by JavaScript.
- they aren't accessible by screen readers.
- they can't be selected by the user when selecting text. (Super useful when you want to add visual content to the text without affecting the text itself when selecting it)
-
When using it, we must provide a
content
property to it in order to work (even if it's empty string""
)div::before { content: ""; width: 100% background-color: red; } /* or */ div::before { content: "sfdsfsdfdsf"; background-color: red; } /* This Won't Work ❌ */ div::before { background-color: red; }
-
they are inline elements, so you can convert them to
inline-block
to control padding -
you can't add pseudo elements like
::before
withimg
element (as it's a content by itself), instead use it on adiv
that contains theimg
-
when you have
pseudo element
like::before
that has no content, you have 2 choices : -
You can use
::before
and::after
to write content after repeated elements like in tables or lists- like unit after the number -> kg, meter,...
- like words before number -> weight:, length:,...
td:nth-of-type(3)::after { content: 'kg'; }
-
Some use-cases:
-
adding quotes to
blockquote
element -
Gradient borders
-
one way is to create a element with
::before
and::after
and give themwidth
andheight
bigger than the element and give thembackground
withgradient
color.box { width: 200px; height: 200px; background-color: #fff; position: relative; } .box::before { content: ''; position: absolute; width: 100%; height: 100%; top: 10px; left: 10px; background: linear-gradient(to right, #f00, #00f); z-index: -1; }
-
another way is to use
box-shadow
withinset
andblur-radius
to create gradient border
-
-
Animating buttons background
.btn::before { content: ''; position: absolute; width: 100%; height: 100%; top: 0; left: 0; background: linear-gradient(to right, #f00, #00f); z-index: -1; transition: opacity 0.3s ease-in-out; opacity: 0; } .btn:hover::before { opacity: 1; }
-
Create custom form controls like toggle checkbox input element
-
-
Notes:
- sometimes you will find it used with single colon
":"
instead of 2"::"
- because in CSS 2 there were no difference between pseudo-classes and pseudo-elements, but in CSS 3 the
"::"
was introduced - Most browsers will still support the single colon syntax for backward compatibility, but it's recommended to use the double colon syntax as some pseudo-elements require it like
::placeholder
.
- because in CSS 2 there were no difference between pseudo-classes and pseudo-elements, but in CSS 3 the
- sometimes you will find it used with single colon
-
If there are two or more CSS rules that point to the same element, the selector with the highest specificity value will "win", and its style declaration will be applied to that HTML element.
-
How does it work
-
- Start at
0
- add
100
for each ID value - add
10
for each class value (or pseudo-class or attribute selector) - add
1
for each element selector or pseudo-element. - The combinators like
+
,>
,~
don't affect the specificity.
- Start at
-
It's similar to Javascript merging objects, where the last object will override the previous one.
p { font-weight: bold; color: hsl(0deg 0% 10%); } .introduction { color: violet; }
/* In order to calculate the final styles const appliedStyles = { ...inheritedStyles, ...tagStyles, ...classStyles, ...idStyles, ...inlineStyles, ...importantStyles }; */ const tagStyles = { fontWeight: 'bold', color: 'hsl(0deg 0% 10%)' }; const classStyles = { color: 'violet' }; const appliedStyles = { ...tagStyles, ...classStyles };
-
-
"Last rule Principle"
- It's a rule in CSS where if multiple rules have the same specificity, then the last rule will be applied.
- So the placement of the rule is important
-
!important
keyword- It gets a specificity score of 10,000 points. This is the highest specificity that one individual item can get.
- It's used to override all other styles, but it's not recommended to use it because it makes the code harder to maintain and update.
- if 2 rules have
!important
, then the last one will be applied (the same as the "last rule principle")
-
Notes:
- The universal selector
*
has no specificity and gets0
points. - It's better to depend on specificity rather than the order of the rules to avoid conflicts.
- Only depend on the order of the rules when you want to override a specific rule. Ex: using 3rd party stylesheets and you want to override some styles by adding your own stylesheet after it.
- Keyframes animations have no specificity, so they can't be overridden by adding
!important
to the styles. Instead, you can use theanimation-name
property to override it.
- The universal selector
-
We shouldn't depend on element-selectors and nested selectors, instead, we should use classes and IDs to style elements.
- This is because it's easier to maintain and update the styles later on without affecting other elements and the HTML structure.
-
id
should be used for unique elements, andclass
for reusable elements.- In the real world, we usually don't use
id
for styling, instead, we useclass
for everything (to be more flexible and reusable). - if you have an
id
that is used twice, the styles will be applied to all elements with the sameid
. but it's not recommended to use the sameid
for multiple elements ❌.
- In the real world, we usually don't use
-
Universal selector (
*
) has no specificity and gets 0 points (least specificity so it's like a fallback or when overriding default styles). -
You can apply the same rule to multiple selectors by separating them with a comma
","
(Grouping selectors).h1, h2, h3 { color: red; }
-
Specificity in CSS only concerns selectors, not their associated declarations. !important applies to a declaration, so it alone plays no role in specificity.
-
more info & examples here css-specificity
The CSS Box Model is a set of rules that describe how elements on a web page are rendered and sized by the browser. It's called a "box model" because every element on a web page is represented as a rectangular box.
-
It makes each element in the HTML document a rectangular box that consists of the following parts: (content, padding, border, and margin).
-
The default behavior of calculating the
width
andheight
of an element is to include the content, padding, and border, but not the margin. This is calledcontent-box
(default value) -
To include the padding and border in the
width
andheight
of an element, you can use thebox-sizing
property with the valueborder-box
.div { box-sizing: border-box; }
-
The old way has
box-sizing: content-box;
as the default value, but now the default value isbox-sizing: border-box;
in most modern browsers. -
In order to add it in the reset CSS (Global Styles), you can use:
html { box-sizing: border-box; font-size: 100%; } *, *::before, *::after { box-sizing: inherit; } /* Don't use it on the body because it won't be inherited, Instead use it on the ":root" or "*" */
-
-
-
Interview question: What is the difference between
box-sizing: content-box;
andbox-sizing: border-box;
?content-box
includes only the content of the element in the width and height calculation, whileborder-box
includes the content, padding, and border in the width and height calculation. So when you useborder-box
, we want to set thewidth
andheight
of the element to include the entire box (border box) not just the content (content box).
-
It's the inner space between the content and the border of an element.
-
Padding can be set for all directions at once, or it can be specified for individual directions:
.even-padding { padding: 20px; } .asymmetric-padding { padding-top: 20px; padding-bottom: 40px; padding-left: 60px; padding-right: 80px; } /* The same thing, but using ✨ logical properties ✨ */ .asymmetric-logical-padding { padding-block-start: 20px; padding-block-end: 40px; padding-inline-start: 60px; padding-inline-end: 80px; }
-
The
padding
shorthand property has a couple tricks up its sleeve. It can be used to set asymmetric padding, in a few different ways..two-way-padding { /* top/bottom = 10px, right/left = 20px */ padding: 15px 30px; } .asymmetric-padding { /* top = 10px, right = 20px, bottom = 30px, left = 40px */ padding: 10px 20px 30px 40px; } .three-way-padding { /* top = 10px, right/left = 20px, bottom = 30px */ padding: 10px 20px 30px; }
- This pattern is shared amongst other CSS properties that have shorthand values. like
margin
,border
,outline
,background
,font
,list-style
,animation
,transition
,grid
,flex
- This pattern is shared amongst other CSS properties that have shorthand values. like
-
Notes
- You will often find usage of Asymmetric Padding in elements that contain text, to give more space at the top and bottom of the text. This gives optical illusion that the text is more centered and easier to read. because if we didn't use it, the text will look like it's sticking to the top of the element because of the text's line-height.
It's the line that surrounds the content and padding of an element.
-
There are three styles specific to border: (width, style, color)
-
They can be combined into a shorthand:
.box { border: 3px solid hotpink; }
-
The only required field is
border-style
. Without it, no border will be shown!.not-good { /* 🙅♀️ Won't work ❌ – needs a style! */ border: 2px pink; } .good { /* 🙆♀️ Will produce a black, 3px-thick border */ border: solid; }
-
-
They can also be used with different values for each direction separately.
.box { border-left-color: red; border-right-color: blue; }
-
If we don't specify a border color, it'll use the font's color by default.
-
If you want to specify this behaviour explicitly, it can be done with the special
currentColor
keyword..box { color: hotpink; border: 1px solid currentColor; box-shadow: 2px 2px 2px currentColor; }
-
It's used to create rounded corners for an element.
It's not actually related to the border, but it's related to the element's corners (the
border-radius
property rounds an element even if it has no border!)
-
You can use the shorthand property
border-radius
to set the radius for all corners at once, or you can specify the radius for each corner separately..box { border-radius: 10px; } .box { border-top-left-radius: 10px; border-top-right-radius: 20px; border-bottom-right-radius: 30px; border-bottom-left-radius: 40px; /* Wrong ❌ */ border-left-radius: 10px; }
-
If you have an element with square dimensions, you can create a circle by setting the
border-radius
to50%
..circle { width: 100px; height: 100px; border-radius: 50%; }
-
outline is outside of the border (and it may overlap with other elements)
-
It has 3 properties: (width, style, color)
div { outline: 2px solid #fff; }
-
Outlines share many of the same properties as borders, but they have a few key differences (outline vs border)
- Outlines don't take up space in the layout, so they won't affect the size or position of the element.
- Outlines can't have rounded corners (now it follows the
border-radius
of the element) - Outlines can have an offset, which allows you to move the outline (either inside or outside the element).
-
Outlines have a special
outline-offset
property. It allows you to add a bit of a gap between the element and its outline.div { outline: 2px solid #fff; outline-offset: 5px; }
-
outline
is an animated property -
Notes:
-
Never apply
outline: none;
to get rid of the default outline on focus, instead, style it to match your design-
This is because the outline is important for accessibility, especially for keyboard users.
-
The only exception is if we provide a suitable alternative. For example:
button { outline: none; } button:focus { background: navy; color: white; }
-
-
It's the space between the border of an element and the surrounding elements.
It's easy to fall into the trap of thinking that margin is exclusively about changing the selected element's position. Really, though, it's about changing the gap between elements.
-
By default, most browsers add a margin to the top of the
<h1>
element. This is why there is a gap between the top of the browser and the box containing the<h1>
element.- That's why we use
* { margin: 0; }
to remove the default margin from all elements when resetting the styles.
- That's why we use
-
margin
can be negative as it's related to the surrounding elements and not the element itself -
When you want to use the
margin
shorthand for all directions, we used to always specify horizontal & vertical spacing, now there're new css-properties calledmargin-inline
andmargin-block
- It's a trick to center an element horizontally by setting the left and right margins to
auto
. - It works by distributing the remaining space equally on both sides of the element. (fill the maximum available space)
- This only works for horizontal margin. Setting top/bottom margin to
auto
is equivalent to setting it to0px
- This only works on elements with an explicit width. Block elements will naturally grow to fill the available horizontal space, so we need to give our element a width in order to center it. (Doesn't work with
width: 100%
)
- This only works for horizontal margin. Setting top/bottom margin to
It's a common issue in CSS when two vertical margins are overlapping, they will collapse (combine) into a single margin.
-
Margins only collapse in Flow layout
- Margin collapse is unique to Flow layout. If you have children inside a
display: flex
parent, those children's margins will never collapse. - It doesn't happen with
flexbox
,grid
, orinline-block
elements.
- Margin collapse is unique to Flow layout. If you have children inside a
-
Here, only one of them will be visible to the page which is the larger one and not their sum (The bigger margin wins)
- This doesn't happen with horizontal margins
- this is not the same for padding as they get added together
- So, between sections use padding not margin
-
Margins must be touching in order for them to collapse
-
Notes:
-
Nesting doesn't prevent collapsing,
-
Margins can collapse in the same direction
.parent { margin-top: 72px; } .child { margin-top: 24px; }
-
More than 2 margins can also collapse
-
For negative margins:
- The negative margins will share a space, and the size of that space is determined by the most significant negative margin.
- For mixed margins (positive and negative), they will be summed together.
-
-
How to fix it
-
Border collapse : sets whether table borders should collapse into a single border or be separated as in standard HTML =>
border-collapse: separate;
-
When selecting an element on the browser, you can see the
box-model
properties of the element in the developer tools, and the browsers highlight the element's box model when you hover over it (light-blue for content area, orange for margin) -
Many developers believe that
pixels
are bad for accessibility. This is true when it comes tofont-size
, but pixels can be the best unit to use forpadding
(and other box model properties likemargin
/border
) because they're fixed and don't change with the user's settings. -
By default, elements have initial values for
margin
&padding
, So at the beginning, we should reset them to0
to avoid any unexpected behavior.* { margin: 0; padding: 0; } /* This won't work ❌ because `margin` and `padding` are not inherited */ body { margin: 0; padding: 0; }
-
auto
value for width/height/margin/paddingmargin: auto
-> centers the element horizontallypadding: auto
-> it's invalid and won't work ❌width / height: auto
-> the width / height of the element will fill the available space (remaining width / height of the parent element)
-
You can overwrite values of the
box-model
properties for specific elements like this:/* ✅ */ .box { padding: 48px; padding-bottom: 0; } /* ❌ because `padding-bottom` comes first, it will be overwritten by the shorthand. */ .box { padding-bottom: 0; padding: 48px; }
-
Color is a combination of hue, saturation, and lightness (
HSL
is a color model that describes color as a combination of hue, saturation, and lightness values)- Hue is the color itself (0-360) degrees
- Saturation is the intensity of the color (0-100%)
- Lightness is the amount of white or black in the color (0-100%)
-
There're many ways to represent colors (most common is RGB Model)
-
- in RGBA,
alpha
channel: Defines the opacity as a number between0.0
(fully transparent) and1.0
(fully opaque)
- in RGBA,
It's a way to specify colors using hue, saturation, and lightness values.
body {
background-color: hsl(0, 0%, 78%);
}
p {
/* hsla color property adds a fourth value which represents transparency (a for alpha). */
background-color: hsla(0, 100%, 100%, 0.5);
}
It's a way to create a gradient that changes color smoothly from one color to another.
-
It's created using the
linear-gradient()
function, which takes two or more color values as arguments.div { background-image: linear-gradient(to right, red, yellow); /* or with multiple colors */ background-image: linear-gradient(to right, red, yellow, green, blue); }
-
You can also specify the direction of the gradient by using the
to
keyword followed by a direction (top, right, bottom, left, or any angle).div { background-image: linear-gradient(to bottom right, red, yellow); /* or */ background-image: linear-gradient(45deg, red, yellow); }
-
You can also specify the position of the color stops by adding a percentage value after the color.
div { background-image: linear-gradient(to right, red 20%, yellow 80%); }
-
We can have more than one gradient in the same element (they will be stacked on top of each other)
div { background-image: linear-gradient(to right, red 20%, yellow 80%), linear-gradient(to bottom, blue, green); }
-
we have the ability to specify a gradient for the background of a box. The gradient is created using the
background-image
property:- to add overlay gradient with one color, you make the linear-gradient with the same value for the 2 gradient colors
div { /* background image (with darkening-overlay) */ background-image: linear-gradient(rgba(34, 34, 34, 0.6), rgba(34, 34, 34, 0.6)), url(hero.jpg); /* here, linear-gradient needs to be in rgba() form to show the image */ }
- Here, the linear-gradient is on top of the background-image, which gives an overlay look.
-
Notes:
- linear-gradient is a
background-image
property, so it can be applied only with either background orbackground-image
properties.background-color
won't work ❌
- you can generate gradients from here cssgradient.io
- There's also
radial-gradient
which is a gradient from the center to the outside
- linear-gradient is a
-
It's a good practice to define colors in a separate file or at the top of the CSS file to make it easier to change them later.
:root { --main-color: #087f5b; /* Main color */ --secondary-color: #343a40; /* accent color */ --light-color: #f8f9fa; /* light color */ }
-
currentcolor
keyword- is a keyword that can be used in place of a color value. It will use the current value of the
color
property.
.box { color: red; border: 2px solid currentColor; }
- is a keyword that can be used in place of a color value. It will use the current value of the
-
If you want colors to adapt with the changing the light/dark mode of the browser, you can:
-
Use
canvas
color instead ofwhite
:root { color-scheme: light dark; } body { color: canvas; }
-
use the
prefers-color-scheme
media query.@media (prefers-color-scheme: dark) { body { background-color: black; color: white; } }
Trick to test it when developing, instead of changing the browser or the OS settings, you apply this code for
light
mode then after finishing, you change it todark
mode
-
-
To check if the element's color's contrast ratio is good for accessibility, you can use the devtools to check the contrast ratio between the text color and the background color. by selecting the element in the inspector and a tooltip will appear with the contrast ratio.
- Good contrast is above
4.5:1
for normal text and3:1
for large text.
- Good contrast is above
-
Color Recourses
- Happy Hues -> color palettes for your projects
-
- is the process of adjusting the spacing between individual characters in a proportional font, usually to achieve a visually pleasing result.
- Browsers use different kerning algorithms, so the text may look different in different browsers.
- Naturally, the browser doesn't do any kerning for
monospaced
fonts, since those characters need to align neatly into columns.
- Naturally, the browser doesn't do any kerning for
- How to control it in CSS -> Line & Letter Spacing
-
Text Rasterization
- is the process of converting text from a vector format (like a font file) into a raster format (like pixels on a screen).
Modern fonts are vector-based (
ttf
,otf
,svg
,woff/woff2
) rather than bitmap-based, allowing them to scale smoothly to any size. The browser converts these vector instructions into pixels through rasterization. -
Font Smoothing
- is the process of anti-aliasing text to make it look smoother on screen.
- It's controlled by the browser and the operating system, so you can't control it with CSS.
When choosing a typeface, it is important to understand that a browser will usually only display it if it's installed on that user's computer or if it is available on the web.
-
Browsers are supposed to support at least one typeface from each of the groups above which is called a "font-stack / generic-family". For this reason, it is common to add the generic font name after your preferred choice of typefaces (in case the preferred typeface is not available or fails to load).
font-family: Georgia, Times, serif; /* Here, "Georgia" is the preferred typeface, "Times" is the backup, and "serif" is the generic font family. */
- This acts as a sort of "preference list". We've given the browser a list of fonts we'd like to use, in priority order. Ideally, it'll pick the first one, but if it's not available, it can use the second, or the third, or the fourth.
-
@font-face
-
It allows you to use a font, even if it is not installed on the computer of the person browsing, by allowing you to specify a path to a copy of the font, which will be downloaded if it is not on the user's machine.
/* Declaring a custom font */ @font-face { font-family: 'ChunkFiveRegular'; src: url('fonts/chunkfive.eot'); font-weight: 400; font-style: normal; } h1, h2 { /* Using the custom font */ font-family: ChunkFiveRegular, Georgia, serif; }
-
Google also provides open source fonts. Rather than adding the
@font-face
rule to your own style sheet, you link to a CSS file and font files on their servers-
in the HTML
head
:<!-- Must be before the styles files --> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto" />
-
in the CSS file:
/* Must be at the top of the CSS file */ @import url('https://fonts.googleapis.com/css?family=Roboto');
-
-
-
when importing font-faces from a link, we have 2 methods:
-
put the
<link>
code in the HTMLhead
before the styles files- This is faster because the browser will download the font file before the CSS file
-
use
@import url()
, but it needs to be the first thing in your css file/s to work correctly- This is useful when you want to use the same css file for multiple projects and you want to keep the font import in the same file
-
-
Performance trade-offs: Self-hosted fonts can be faster than Google Fonts because:
-
Google Fonts requires multiple requests:
- Request to get the CSS file from
fonts.googleapis.com
- Request to get the font file from
fonts.gstatic.com
- Request to get the CSS file from
-
These external requests add overhead and block page rendering due to "handshaking" with the server.
-
With self-hosted fonts:
- CSS can be embedded directly in HTML
- Font files are served from same domain
- Fewer requests and less overhead of reaching out to external servers
- save a network request (instead of fetching the stylesheet from Google Fonts, then fetching the font files from Google's CDN), here we fetch the font files directly from our server.
/* Self-hosted font */ @font-face { font-family: 'Roboto'; src: url('/fonts/roboto.woff2') format('woff2'); /* 👈👈👈 */ font-weight: 400; font-style: normal; }
If you're using Next.js 11 or higher, the framework will automatically optimize this for you.
Angular v11+ has built-in support for Google Fonts, with configuration for inlining the fonts directly. You can learn more in the Angular docs.
-
-
When a user visits a website using web fonts for the first time, they need to download the font files. During this download, browsers handle text rendering in two ways:
-
FOIT (Flash Of Invisible Text)
- Browser hides text until font loads
- User can't read content while waiting
- Default behavior in most browsers
-
FOUT (Flash Of Unstyled Text)
- Browser shows text in fallback font first
- Switches to web font when loaded
- Can cause layout shifts
-
Note: Font loading issues may not be noticeable during development because:
- Fonts download instantly on localhost
- Browsers cache fonts after first visit
- The font may already be installed on your machine
- Fast internet connections mask loading delays
Always test font loading behavior in production with various connection speeds.
-
To control this behavior, use the
font-display
property (It's included in our @font-face statement)@font-face { font-family: 'Great Vibes'; src: url('/fonts/great-vibes.woff2') format('woff2'); font-weight: 400; font-style: normal; font-display: swap; /* 👈👈👈 */ }
-
When we add the Google Font snippet, it includes a query parameter that sets this property:
-
The
font-display
property controls how fonts load through 3 time periods:- Block: Text is invisible while waiting for font to load
font-display: block;
-> it priotizes the availability of the font over the speed of loading the page.- It should only be used when the font is essential for the user experience (ex: icons, logos, etc.)
- Swap: Fallback font shows until web font loads and swaps in
font-display: swap;
-> it priotizes the speed of loading the page over the availability of the font.- It's the best option for most websites because it provides a good balance between performance and user experience.
- This is the value that Google Fonts uses. It's a good option, but I think there's a better one for most cases.
- Failure: If font hasn't loaded by now, fallback font remains
font-display: fallback;
-> it's the preferred value. I use it in all of my projects. It prioritizes a smooth user experience above all else:- It features a very-short block period (about
100ms
), and a moderate swap period (about3s
). - On speedy connections, it's likely that the font can be downloaded within the block period, preventing an uncomfortable flash between font families
- On very slow or intermittent connections, the fallback font is used forever, preventing a random flash between fonts seconds/minutes after the page has loaded.
- Optional: Font is optional and won't block rendering
font-display: optional;
-> it's the least preferred value. It's not recommended because it doesn't guarantee that the font will be loaded at all. It's only useful for decorative fonts that don't affect the user experience.
- Block: Text is invisible while waiting for font to load
-
-
font properties:
font-weight
: specifies the boldness of the font- normal ->
400
- bold ->
700
- normal ->
font-style
: specifies the style of the fontfont-variant
: specifies whether or not a text should be displayed in a small-caps fontfont
: shorthand property for the font-style, font-variant, font-weight, font-size, line-height, and font-family properties
-
text-transform
property:-
It is used to change the case of the text.
-
It can have the following values:
uppercase
-> transforms the text to uppercaselowercase
-> transforms the text to lowercasecapitalize
-> transforms the first character of each word to uppercasenone
-> no transformation
p { text-transform: uppercase; }
-
-
text-decoration
property:-
It is used to specify the decoration added to text.
-
It can have the following values:
none
-> no decorationunderline
-> underlines the textoverline
-> overlines the textline-through
-> draws a line through the textblink
-> makes the text blinkrevert
-> reverts to the default decoration (usually when you want to remove/show the underline of a link when hovering)
p { /* line line-style line-color */ text-decoration: underline wavy red; /* or just */ text-decoration: underline; }
-
Instead of using the shorthand
text-decoration
, you can use the following properties:span { /* same as text-decoration but with more options */ text-decoration-line: underline; text-decoration-style: wavy; text-decoration-color: red; }
-
-
text-shadow
property -> text-shadow
-
-
text-indent
div { text-align: left; text-indent: 6rem; }
-
or select the first letter in the paragraph and give it a margin
p::first-letter { margin-left: 1em; }
-
-
text-align
:-
It is used to specify the horizontal alignment of text.
-
It can have the following values:
left
-> aligns the text to the leftright
-> aligns the text to the rightcenter
-> aligns the text to the centerjustify
-> aligns the text to both the left and right margins, adding extra space between words as necessarystart
-> aligns the text to the start of the line (useful for languages that are read from right to left)end
-> aligns the text to the end of the line (useful for languages that are read from right to left)
p { text-align: center; }
-
For languages that are read from right to left (like Arabic), the
left
value will align the text to the right, and theright
value will align the text to the left.body { direction: rtl; }
-
-
vertical-align
:-
is a common source of confusion. It is not intended to allow you to vertically align text in the middle of block level elements such as
<p>
and<div>,
although it does have this effect when used with table cells (the<td>
and<th>
elements). -
It is more commonly used with inline elements such as
<img>,
<em>,
or<strong>
elements. When used with these elements, it performs a task very similar to the HTML align attribute used on the<img>
elementimg { vertical-align: middle; }
-
-
Line length
-
The ideal line length is between
45
and75
characters per line. -
fortunately, CSS has a build-in unit for this:
ch
(character width)p { max-width: 50ch; }
-
Does setting a width of
50ch
for example mean that we'll get an average of50
characters per line?- No, because the width of each character is different, so it's just an approximation. It depends on the font and the characters used.
-
-
line-height
-
It's a term to control "Leading" (pronounced "ledding")
-
it is a term typographers use for the vertical space between lines of text, and doesn't affect the
font-size
-
if we didn't use a unit then it's relevant to the current font size
p { font-size: 20px; line-height: 1.5; /* 30px */ }
-
⚠️ It's acts differently withJSX
(React)
-
-
letter/word-spacing
-
-
is the term typographers use for the space between each letter.
-
You can control the space between each letter with:
-
font-kerning
property, but it's not widely supported, instead, you can use theletter-spacing
property which acts as a "kerning multiplier" (it amplifies or reduces whatever kerning the browser has set for the font). -
For custom control, follow these steps:
<p> <span>H</span> <span>e</span> <span>l</span> <span>l</span> <span>o</span> </p> <style> p { font-size: 20px; font-kerning: none; } span:nth-child(1) { letter-spacing: 0.1em; } span:nth-child(2) { letter-spacing: 0.2em; } span:nth-child(3) { letter-spacing: 0.3em; } </style>
- disable the browser's default kerning by setting
font-kerning: none;
- wrap each letter in a
<span>
element - apply a
letter-spacing
value to the<span>
elements, picking custom values for each letter
- disable the browser's default kerning by setting
-
-
-
When you specify a value for these properties, it should be given in
em
p { letter-spacing: 0.1em; word-spacing: 0.2em; }
-
The default gap between words is set by the typeface around
0.25em
-
We can use negative values to make the letters overlap
p { letter-spacing: -0.1em; }
-
When text overflows the container, you can control how it behaves using the overflow
property or by controlling how it wraps when it reaches the end of the container.
-
How the browser identify that the text is overflowing the container?
-
The browser identifies overflow by grouping characters into unbreakable "words" (like
"https://www.google.com"
) and looking for "soft wrap opportunities" (likespaces
ordashes
) where it can split content onto the next line when it would spill outside the container. If there are no soft wrap opportunities, the browser will overflow the content. -
Note that there're "non-breaking spaces" (
) that the browser won't break on them (it's like a normal space but it won't break the line)<p>That sandwich costs $10 USD, or you can barter for it.</p>
-
Adobe has made a CSS proposal to support an alternative text-placement algorithm. They have a JavaScript polyfill you can use today, and the results are pretty impressive
-
-
It specifies what should happen if content overflows an element's box.
-
visible
-> the text will overflow the container (Default)hidden
-> the text will be hidden- Usually used when we want to truncate the text with ellipsis
(...)
- Usually used when we want to truncate the text with ellipsis
scroll
-> the text will be hidden but a scrollbar will appear- Usually used when we are sure that the text will overflow the container, and we want to show the scrollbar from the beginning
auto
-> the text will be hidden but a scrollbar will appear (only if needed)- Usually used when we are not sure if the text will overflow the container, and we want to show the scrollbar only if needed
-
Notes:
- Why use
overflow: scroll
instead ofoverflow: auto
?- In most cases,
overflow: auto
is a better choice thanoverflow: scroll
, but as with everything, there are tradeoffs involved.- Inside an
auto
container, a layout shift will occur when the content grows to exceed the available space; the content box shrinks by~15 pixels
, the width of the scrollbar. - This can be a bit jarring, so if you know that a container will need to scroll, using
overflow-y: scroll
can make for a smoother experience.
- Inside an
- In most cases,
- If you set
overflow-x
tohidden
andoverflow-y
tovisible
, the text will be hidden horizontally but will show a vertical scrollbar as if it hasoverflow-y: scroll
.- This behavior occurs because the element becomes a scroll container, and its children are confined within its bounds. The
overflow-x: hidden
removes the horizontal scrollbar, whileoverflow-y: visible
allows vertical scrolling, but the content is still confined within the container.
- This behavior occurs because the element becomes a scroll container, and its children are confined within its bounds. The
- Why use
with overflow-wrap
property, you can control how the text wraps when it reaches the end of the container.
- Values:
overflow-wrap: break-word
-> the text will wrap to the next line if it reaches the end of the container, even if it means breaking a word in half.- this declaration gives the browser permission to break after any character
- Note that the split will happen without any visual indication that the word has been broken, but in print mode, the word will be hyphenated with
"-"
(it's also controllable withhyphens
property)
overflow-wrap: normal
-> the text will wrap to the next line if it reaches the end of the container, but it will not break a word in half.
- It's called
word-wrap
in some browsers (like IE)
-
clip
-> the text will be clipped -
ellipsis
-> the text will be clipped and an ellipsis will be shown to indicate that there is more text -
text-wrap
property:normal
-> the text will wrap to the next linenowrap
-> the text will not wrap to the next linepre
-> the text will wrap to the next line but will respect the line breaks in the textpre-wrap
-> the text will wrap to the next line and will respect the line breaks in the textpre-line
-> the text will wrap to the next line and will respect the line breaks in the text but will collapse multiple spaces into one
If you want to add an ellipsis to a multi-line text, you can use the following trick:
p {
display: -webkit-box;
-webkit-line-clamp: 3; /* number of lines to show */
-webkit-box-orient: vertical;
overflow: hidden; /* we need to hide the lines that will be clamped off */
}
- Notes:
- Apply this code to the element that contains the text you want to clamp. and not the container of the element.
- In certain cases, it can be buggy, because the element that we're applying this code is also a
flex
container, so it may not work as expected.- To avoid possible issues, always apply line clamping to a paragraph tag that isn't being stretched or flexed as part of
flexbox
orCSS Grid
. We can solve for this by using a wrapperdiv
- This is because,
clamp
doesn't remove the text, it just hides it, by trying to figure out the height of the text and then hiding the rest of it. and if you specify a bigger height than the text, it might not work as expected.
- To avoid possible issues, always apply line clamping to a paragraph tag that isn't being stretched or flexed as part of
-
You can use the shorthand
font
property to set all the font properties at once.p { /* font-style font-variant font-weight font-size/line-height font-family */ font: italic bold 20px/30px Georgia, serif; }
-
white-space: no wrap
=> this forces it to automatically go to next line when reachingmax-width
-
text-overflow:ellipsis
=> when text passes themax-width
, it shows this...
at themax-width
limit as indication of more text available -
Viewport Units: They create layouts that depend on the screen size
-
values from 0 to 100
-
vh
: height (percent of the screen) -
vw
: width (percent of the screen) -
To get the rest of screen's width/height, you can use the calc()
.banner { height: calc(100vh -100px); }
-
-
If you're getting your fonts from a service like https://fonts.google.com, note that there may be some tracking of IP-Addresses of the users when the
HTML
page is downloaded in the browser- to avoid this, you can download the font files locally and refer to it in the
HTML
file - more info here
- to avoid this, you can download the font files locally and refer to it in the
-
to make text
uppercase / lowercase / capitalize
, you can usetext-transform
property -
Font Recourses
- Typescale.com -> allows you to see how different font sizes look together, and modify the scale to fit your design, and then copy the CSS code for the font sizes you want to use in your project 🚀
- For more fun font use => Rubik
Units are used to specify the size of elements or values of properties in CSS.
- Types of units:
- Measurements:
- Relative Units (
em
,rem
,%
,vw
,vh
) - Absolute Units (
px
,pt
,in
,cm
,mm
)
- Relative Units (
- Keywords:
auto
fit-content
min-content
max-content
- Measurements:
- The most popular unit for anything size-related is the pixel (
px
), but there are other units that can be used in CSS. - How units are converted to pixels:
-
Setting font size in pixels is the best way to ensure that the type appears at the size you intended (because percentages and ems are more likely to vary if a user has changed the default size of text in their browser).
-
Pixels are nice because they correspond more-or-less with what you see on the screen. It's a unit that many developers get comfortable with.
-
Problem with pixels is that they don't scale well with the user's preferences (if the user changes the default font size in the browser settings, the pixel size won't change) -> Bad for Accessibility
The percentage %
unit is sometimes a value from the parent and other times it's a value from the element itself.
-
For
width
/height
, It's a way to consume a portion of the available space. (relative to the parent element's size)div { width: 50%; }
-
For
font-size
, or text related properties, it's relative to the parent element's font size.p { font-size: 120%; line-height: 50%; }
em |
rem |
---|---|
relative to the font-size of the element itself (current element) | relative to the font-size of the root element (html ) |
if used in the font-size property, it's relative to the parent font-size |
if used in the font-size property, it's relative to the root font-size |
if used in the length property, it's relative to the current font-size within element |
if used in the length property, it's relative to the root font-size |
-
em
is not generally recommended forfont-size
❌ because it can lead to compounding of font sizes (if you have nested elements withem
font sizes, the font size will compound and get larger and larger as you nest more elements)html { font-size: 10px; /* 1rem = 10px */ } body { font-size: 1.6rem; /* 16px */ } h1 { font-size: 2.4rem; /* 24px */ } p { font-size: 1.4rem; /* 14px */ }
-
rem
(more recommended ✅) ->- It's relative to the (
root
/html
) element'sfont-size
- It's used to avoid the compounding effect of
em
units- It behaves consistently and predictably, like
pixels
, but it respects user preferences when it comes to increasing/decreasing default font sizes.
- It behaves consistently and predictably, like
- It's easier to manage & maintain as we can refactor the whole design by changing the
font-size
of thehtml
element only
- It's relative to the (
-
vh
-> 1% of the viewport's height -
vw
-> 1% of the viewport's widthdiv { width: 50vw; height: 50vh; }
-
They're used to create layouts that depend on the screen size
vh
is useful for creating full-screen sectionsvw
is useful for creating layouts that depend on the screen size
-
There are also 2 more viewport units:
vmin
andvmax
(not used as much)vmin
-> the smaller ofvw
andvh
vmax
-> the larger ofvw
andvh
-
auto
value for width/heightwidth: auto
-> the width of the element will take the available space of the parent element
-
min-content
-
width: min-content
-> the width of the element will be the minimum required to fit the content inside it (it will shrink to the minimum width of the content) -
Here, we aren't sizing based on the space made available by the parent, we're sizing based on the element's children!
-
It checks for any chance that the content might overflow and makes sure it fits within the parent element (white space between words) forcing the text to wrap to the next line if needed
-
Use-cases:
-
-
max-content
-
fit-content
value for widthwidth: fit-content
-> the width of the element will be the minimum required to fit the content inside it (But it won't make block elements inline even if the content is small)
-
we shouldn't use Pixels for font-sizes as if user changed his browser base font-size (the default is 16px), then it won't reflect on the page. Instead we use a relative unit that can scale up/down like:
rem
orem
-
You shouldn't actually set a
px
font size on thehtml
tag-
This will override a user's chosen default font size in their browser settings which will affect using
rem
units -
Instead use percentage instead of
px
html { font-size: 100%; /* or */ font-size: 62.5%; /* Now -> 1rem = 10px */ }
-
-
Fun fact: when selecting the
html
tag,em
andrem
are the same because they're both relative to thehtml
tag -
There are other units like:
vw
andvh
-> relative to the viewport sizept
-> point (1/72 of an inch)in
-> inch (for print styles)
- To add shadows to elements, you can use the
box-shadow
property. - To add shadows to text, you can use the
text-shadow
property.
The first value is the horizontal offset, the second value is the vertical offset, the third value is the blur radius, and the fourth value is the color of the shadow.
div {
/* x-offset y-offset blur-radius color */
box-shadow: 10px 10px 5px rgba(0, 0, 0, 0.1);
}
-
x-offset
: means in x-direction (horizontal) -
y-offset
: means in y-direction (vertical) -
blur-radius
: strength of blueness (Optional)- If omitted, the shadow is a solid line like a border
-
spread-radius
: expand or grow the radius of the shadow in all directions (Optional) -
color
: the color of box-shadow (Optional) -
inset
to add border from the inside and not outside (Optional) => ex:box-shadow: inset 0 0 0 3px ##7cc0e7;
-
sometimes it's used instead of border, as it doesn't affect the size of the element and doesn't cause shifting of other elements
.btn:hover { box-shadow: 0 2px green; }
-
Example:
.myClass { /* x y blur-radius color */ text-shadow: 1px 1px 0px #ff0000; }
-
you can add multiple text-shadows to the same element by separating them with a comma
","
.myClass { text-shadow: 1px 1px 0px #ff0000, 2px 2px 0px #0000ff; }
To generate text-shadow -> Here
The <img>
tag is a replaced element. The browser replaces it with the actual image, which behaves differently from regular DOM nodes. For example, images can have heights set, unlike most inline elements.
Images have intrinsic sizes. These dimensions come from the image file itself, like a 400 × 300 resolution. This impacts how they interact with layouts.
-
Images also have an intrinsic aspect ratio. This means that if we only supply one dimension, either
width
orheight
, the other dimension scales up or down as-needed, to preserve the aspect ratio: (if we set thewidth
to200px
, theheight
will be calculated automatically to preserve the aspect ratio)- What happens if we provide both a width and a height? What if it doesn't match the image's natural aspect ratio? By default, the image will be distorted, stretching like a sock being pulled over a foot
- That said, this default behaviour is almost never what we want it to do. A better alternative in most cases is to crop the image, trimming off any excess that can't fit in the specified rectangle.
- For many years, this was only possible with background images. Fortunately, modern CSS includes a couple tools that can help us out in this case. First, let's look at
object-fit
-
object-fit
property-
It is used to specify how an
<img>
or<video>
should be resized to fit its container and to maintain aspect ratio. (specially when the image is bigger than the container or when resizing the browser window) -
This property tells the content to fill the container in a variety of ways; such as "preserve that aspect ratio" or "stretch up and take up as much space as possible".
-
fill
- This is the default value which stretches the image to fit the content box, regardless of its aspect-ratio.contain
- This value will resize the image to fit the content box, maintaining its aspect ratio, even if it means leaving some empty space.cover
- This value will resize the image to cover the content box, cropping the image if needed and maintaining its aspect ratio.none
- This value will not resize the image at all.scale-down
- This value will compare the difference betweennone
andcontain
, and the smaller one will be used.
-
we use it to use images with specified
width
&height
without distorting them (prevent the image from being stretched or distorted)img { width: 200px; height: 300px; object-fit: cover; }
-
-
-
When using an
object-fit
value likecover
, the browser will crop our image so that we see the very center of the image, and trim off the edges. But in some cases, we'll want to use a different anchor point. -
object-position
lets us specify how the image should be translated or cropped within its available space. It's very similar tobackground-position
property. -
It takes 2 numbers: the first is the horizontal position, and the second is the vertical position. The values can be percentages, lengths, or keywords.
img { object-fit: cover; object-position: 50% 50%; /* or */ object-position: 45px 10px; /* or */ object-position: right bottom; }
-
Note: you can normally use
object-position
withoutobject-fit
, but it won't have any effect unless you're usingobject-fit: none;
-
-
aspect-ratio
property-
It is used to specify the aspect ratio of an element.
-
It's a new property in CSS that allows you to set the aspect ratio of an element. It's useful when you want to maintain the aspect ratio of an element, like an image or a video, even when the size of the element changes (fluid layout).
-
It's used when we don't want to specify fixed
width
andheight
for the image, but we want to maintain the aspect ratio of the image in fluid layouts where the size of the image changes based on the size of the container.img { width: 100%; aspect-ratio: 16 / 9; }
-
Another use-case is when we have multiple images with different sizes and we want to make them have the same aspect ratio.
img { width: 100%; aspect-ratio: 1 / 1; }
-
Here, we had multiple images with different widths & heights, and when we set the
width
to100%
, the images had the same width but different heights, so we usedaspect-ratio
to make the images have the same aspect ratio. -
Another enhancement would be to only apply the
aspect-ratio
property if it's supported by the browser, so we can use the@supports
rule.
-
-
-
transform
property is used to rotate, scale, skew, or translate an element. -
scale
-> to resize the imageimg { transform: scale(1.5); }
-
Trick to make the image zoomed-in when hovering over it
img { transition: transform 0.5s; } img:hover { transform: scale(1.5); } .img-container { overflow: hidden; /* To hide the overflowed part which gives the zoom effect */ }
-
The background
properties are used to set the background for an element, which can be a color, an image, or a gradient.
-
You can have multiple backgrounds for an element by separating them with a comma
,
div { background: url('img_flwr.gif') no-repeat center center, linear-gradient( to right, #ff0000, #0000ff ); }
-
background-image
-
It is used to set the background image of an element.
-
The image is placed on top of the background-color, and its position is relative to the element's padding box.
body { background-image: url('img_flwr.gif'); }
-
In addition to accepting the URL to an image file, the background-image property also accepts gradients. Here is a neat generator tool: “Magic Pattern” CSS background patterns
-
-
background-size
-
It specifies the size of the background images same as
object-fit
for<img>
elements -
Values:
-
contain
- Scale the image, while preserving its intrinsic aspect ratio (if any), to the largest size such that both its width and its height can fit inside the background positioning area.
-
cover
- It makes the image cover the whole area of its container without distorting it.
- We won't see the whole image, but it will cover the whole area (cropping the image)
- It scales the image, while preserving its intrinsic aspect ratio (if any), to the smallest size such that both its width and its height can completely cover the background positioning area.
- It makes the image cover the whole area of its container without distorting it.
-
auto
: The background image is displayed in its original size. -
value
: The first value is the width and the second value is the height of the image.div { background-image: url('img_flower.png'); background-size: 100px 50px; /* or */ background-size: 100% 50%; }
-
-
-
background-repeat
-
By default, as the element gets bigger, if the background-image is not big enough, it will repeat to fill the entire element(box). to control that, you can use
background-repeat
propertybody { background-image: url('images/header.gif'); background-repeat: repeat-x; /* or: The image is only shown once. background-repeat: no-repeat; */ }
- `
-
-
background-position
-
When an image is not being repeated, It's likely that the image is (bigger or smaller) than the element, so we can control the position of the image inside the element using
background-position
property -
It specifies where in the element container the background image should be placed.
-
This property usually has a pair of values. The first represents the horizontal position and the second represents the vertical.
[top, bottom, left, right, center]
- for example, when selecting
top
, it will align the top of the image with the top of the element
- for example, when selecting
- length values from the top-left
20% 70%;
- absolute length values
1rem 2rem
body { background-image: url('images/tulip.gif'); background-repeat: no-repeat; background-position: 50% 0%; /* or */ background-position: center top; }
-
Note: If you only specify one value, the second value will default to center.
-
-
background-clip
property-
It specifies the painting area of the background of an element. meaning that it specifies the area where the background image or color is painted.
-
It's usually used when you have a border around the element and you want the background to extend under the border or not.
-
border-box
-> The background extends to the outside edge of the border (but underneath the border in z-order).padding-box
-> The background extends to the outside edge of the padding.content-box
-> The background is painted within (clipped to) the content box.
-
Ex: making the background image extend under the border or not
div { background-image: url('img_flwr.gif'); background-repeat: no-repeat; background-position: right top; background-attachment: fixed; background-clip: content-box; }
-
-
background-attachment
-
It specifies whether a background image should stay in one position or move as the user scrolls up and down the page (adds a parallax effect).
-
It can have one of two values:
fixed
scroll
body { background-image: url('img_tree.png'); background-repeat: no-repeat; background-position: right top; background-attachment: fixed; /* or */ /* background-attachment: scroll; */ }
-
-
background
shorthand property -
Notes:
-
when you have an empty div that have a
background-image
and want to make it available for screen readers for SEO -> add these attributes: [role
,aria-label
], ex:<div class="cta-img-box" role="img" aria-label="Woman enjoying food ></div>
-
The filter
property in CSS is used to apply visual effects to an element. It can be used to adjust the color, contrast, brightness, and more.
-
We can change color of image using
filter
property-
make image color black
img { filter: invert(100%); /* or */ filter: brightness(0); }
-
make image color grey =>
img { filter: grayscale(100%); } /* or */ img { filter: brightness(0.5); } /* or */ img { filter: contrast(0.5) brightness(0.5) saturate(0.5); }
-
-
To blur the image, you can use the
blur
valueimg { filter: blur(5px); }
-
filter
property can be used withhover
to make the image change when hovering over itimg { filter: grayscale(100%); transition: filter 0.5s; } img:hover { filter: grayscale(0%); }
The clip-path
property in CSS is used to clip an element to a specific shape or path by specifying a clipping region.
-
It's used to create shapes like circles, ellipses, polygons, and more.
-
It works by controlling each point of the shape (x, y) and the radius of the shape
-
Example:
div { clip-path: circle(50% at 50% 50%); clip-path: polygon(10% 10%, 90% 10%, 90% 90%, 10% 80%); }
-
clip-path
tool => clippy -
Note that a similar result can be done using pseudo elements
::before
&::after
.box::before { content: ''; position: absolute; width: 100%; height: 100%; top: 0; left: 0; background: linear-gradient(to right, #f00, #00f); z-index: -1; transform: skewY(-5deg); transform-origin: top left; /* control the anchor point to make the skewing from the top left corner */ }
The mask
property in CSS is used to hide or show parts of an element by masking or clipping the image at specific points.
mask-image
-> to specify the image to be used as a maskmask-size
-> to specify the size of the mask imagemask-repeat
-> to specify how the mask image should be repeatedmask-position
-> to specify the position of the mask imagemask-origin
-> to specify the origin of the mask image
div {
background-color: red;
width: 200px;
height: 200px;
mask-image: url('mask.png');
mask-size: cover;
mask-repeat: no-repeat;
}
-
Usually when using images, it's better to use it inside a wrapper
div
to control the image size and position and to avoid the image to overflow the container -
img
is an inline element, so:- it's effected by
text-align
, so you can center it using this, unlike block elements where it just center the text inside the element
- it's effected by
-
If you find that the image is big and missing up the layout, you can wrap it inside a
div
and give it awidth
of100%
and set a width for the container<div class="img-container"> <img src="img.jpg" alt="image" /> </div>
.img-container { width: 250px; } img { width: 100%; }
-
if you have an empty space between the image and the bottom-border, This is because the browser treats inline-elements (
display: inline
) as if the're typography, so it adds a space at the bottom as if it's a letter. (This also happens forSVG
elements because they're inline elements)- To fix this, you can:
- make the image a block or inline-block element
- set the
line-height
of the wrapper to0
- make sure that you're using
box-sizing: border-box;
- To fix this, you can:
-
img
element can't have::before
or::after
pseudo-elements because it's a replaced element (it's not a real element in the DOM, it's replaced by the image) -
In order to change color or
svg
image color, you can usestroke
orfill
propertiessvg { color: red; /* Won't work ❌ */ fill: red; /* Works ✅ */ }
-
Images with Flexbox:
-
Note that the default value of
align-items
isstretch
, so the image will stretch to fill the height of the container, which will distort the image. To fix this, you can setalign-items: flex-start;
to make the image keep its original size. or usemin-width: 0;
to allow the image to shrink if needed.container { display: flex; align-items: flex-start; /* or */ min-width: 0; }
-
The better solution is to wrap the image inside a
div
and set theimg
towidth: 100%
to make it responsive<div class="flex-container"> <div class="img-container"> <img src="img.jpg" alt="image" /> </div> </div> <style> .flex-container { display: flex; } .img-container { width: 250px; } img { width: 100%; } </style>
- You might think that this is not semantic, but it's okay as
div
is meant to "divide" the content, so it's okay to use it for this purpose
- You might think that this is not semantic, but it's okay as
-
Gives us the ability to do math in css, usually used with width
and height
properties
-
compatible with
length
,frequency
,angle
,time
,number
andinteger
-
commonly used with mixed units like percentages
%
andvw
units togetherdiv { width: calc(100% - 50px); } .nav { height: 6rem; } header { height: calc(100vh - 6rem); /* 100% of the viewport height - navbar height */ }
-
Advantages
-
can mix different units when performing calculations (not possible in
Sass
).thing { width: 60%; /* fallback if needed */ width: calc(100% - 3em); }
-
make math easier to understand
/* instead of this*/ .column-1-7 { width: 14.2857%; } /* we can do this*/ .column-1-7 { width: calc(100% / 7); /* 1/7th of the available space */ }
-
It can be used with css-variables
:root { --main-width: 100px; } div { width: calc(var(--main-width) * 2); }
-
Calculating colors and gradients
:root { --red-hue: 0deg; --intense: 100% 50%; --red: hsl(var(--red-hue) var(--intense)); --orange: hsl(calc(var(--red-hue) + 20deg) var(--intense)); --pink: hsl(calc(var(--red-hue) - 40deg) var(--intense)); }
-
Doing complex and sequence animations
-
Here, we can use
calc()
to calculate the properties of the animation<style> html { --box-size: 75px; } @keyframes slowtate { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } .box { width: var(--box-size); height: var(--box-size); background: linear-gradient( calc(135deg - var(--index) * 33deg), hsl(calc(275deg + var(--index) * -1deg) 100% 50%), hsl(calc(340deg + var(--index) * -10deg) 100% 50%) ); animation: slowtate calc(500ms + var(--index) * 100ms) 2 linear; } .box:nth-of-type(3n) { border-radius: 25%; } .row { display: flex; flex-wrap: wrap; width: calc(var(--box-size) * 4); height: calc(var(--box-size) * 4); } /* Attach a unique index to each box. Normally, we would do this in JS. */ .box:nth-of-type(1) { --index: 1; } .box:nth-of-type(2) { --index: 2; } .box:nth-of-type(3) { --index: 3; } .box:nth-of-type(4) { --index: 4; } .box:nth-of-type(5) { --index: 5; } .box:nth-of-type(6) { --index: 6; } .box:nth-of-type(7) { --index: 7; } .box:nth-of-type(8) { --index: 8; } .box:nth-of-type(9) { --index: 9; } .box:nth-of-type(10) { --index: 10; } .box:nth-of-type(11) { --index: 11; } .box:nth-of-type(12) { --index: 12; } .box:nth-of-type(13) { --index: 13; } .box:nth-of-type(14) { --index: 14; } .box:nth-of-type(15) { --index: 15; } .box:nth-of-type(16) { --index: 16; } * { box-sizing: border-box; } </style> <div class="row"> <div class="box"></div> <div class="box"></div> <div class="box"></div> <div class="box"></div> <div class="box"></div> <div class="box"></div> <div class="box"></div> <div class="box"></div> <div class="box"></div> <div class="box"></div> <div class="box"></div> <div class="box"></div> <div class="box"></div> <div class="box"></div> <div class="box"></div> <div class="box"></div> </div>
-
-
-
Tips when using
calc()
- You can use 4 basic operators:
+
,-
,*
,/
- White space is required on both sides of the
+
and-
operators, but not required for*
and/
operators - Division by zero will cause errors
- You can use 4 basic operators:
The clamp()
CSS function clamps a middle value within a range of values between a defined minimum bound and a maximum bound. The function takes three parameters: a minimum value
, a preferred value
, and a maximum allowed value
.
font-size: clamp(1rem, 2.5vw, 2rem);
-
It works quite a bit like the trio of
min-width
,width
, andmax-width
, but it combines it into a single property value. In other words, these two rules are functionally identical:/* Method 1 */ .column { min-width: 500px; width: 65%; max-width: 800px; } /* Method 2 */ .column { width: clamp(500px, 65%, 800px); }
-
It works with other properties!
- Historically, we've only been able to limit
width
s andheight
s. There is nomin-padding
/max-padding
ormin-font-size
/max-font-size
. - The amazing thing about
clamp
is that it's a value, not a property. This means that it can be used with just about any property!
- Historically, we've only been able to limit
-
Common Use-cases:
-
Responsive Typography:
font-size: clamp(1rem, 2.5vw, 2rem);
-
Responsive Layouts:
width: clamp(500px, 65%, 800px);
-
Responsive Spacing:
padding: clamp(1rem, 2.5vw, 2rem);
-
Responsive Grid/Flex Gap: (usually in navbar items)
gap: clamp(1rem, 2.5vw, 2rem);
-
-
clamp
allows us to specify a lower and upper bound. But what if we only want to limit one end?- That's where min & max come in!
-
You can use the clamp-calculator to calculate the values
The min()
and max()
CSS functions can be used to set the minimum or maximum value from a list of comma-separated expressions.
width: min(100%, 500px);
/* Instead of */
width: 100%;
min-width: 500px;
-
Trick: we can use it with
min-width
andmax-width
to have multiple min-widths for different screen sizes and use the smaller/bigger one based on the screen size.image { margin: 0 auto; mix-width: min(100%, 500px); /* This means that for smaller screens, the image will be 100% of its container, but for larger screens, it will be a maximum of 500px so that it will be centered */ }
-
Note:
min()
,max()
, andclamp()
will automatically resolve anycalc-
style equations inside them. This helps to keep the code clean and easy to read.width: min(100%, calc(100% - 50px)); /* works ✅ */ width: min(100%, 100% - 50px); /* also works ✅ */
data attributes are plain HTML attributes, you can access them from CSS.
-
For example to show the parent data on the article you can use generated content in CSS with the
attr()
function: -
HTML :
<article id="electric-cars" data-columns="3" data-index-number="12314" data-parent="cars"> ... </article>
-
CSS
article::before { /* use value from data-attribute */ content: attr(data-parent); } /* select element based on data-attribute */ article[data-columns='3'] { width: 400px; }
note: you can't set value of data attribute to false (in HTML or Javascript), instead you delete it
-
empty-cells : If you have empty cells in your table, then you can use the
empty-cells
property to specify whether or not their borders should be shown.table.one { empty-cells: show; } table.two { empty-cells: hide; }
-
Gaps Between Cells : The
border-spacing
property allows you to control the distance between adjacent cells. By default, browsers often leave a small gap between each table cell, so if you want to increase or decrease this space then theborder-spacing
property allows you to control the gap.table.one { border-spacing: 5px 15px; } table.two { border-collapse: collapse; }
-
You can select the
td
andth
elements using pseudo-classes like:first-child
,:last-child
,:nth-child()
,:nth-of-type()
,:nth-last-child()
,:nth-last-of-type()
tr td:nth-child(even) { background-color: #f2f2f2; } td:last-of-type { background-color: #f2f2f2; }
-
Note => in some browsers,
input
,select
,placeholder
don't inherit font properties from their parent, so you will have to do it manually usinginherit
for the value of the properties..cta-form input, .cta-form input::placeholder, .cta-form select { font-family: inherit; color: inherit; }
-
to select the
input-placeholder
in css we usepseudo element
-->input::placeholder
.cta-form input::placeholder { color: #aaa; }
-
:focus
can be used to style the input when it's focused.cta-form input:focus { border: 2px solid #087f5b; /* or */ outline: 2px solid #087f5b; /* or */ box-shadow: 0 0 10px #087f5b; /* this gives you more control like rounded corners using blur-radius */ }
-
To create custom radio buttons, you can hide the default radio button and style the label to look like a button
<input type="radio" id="radio1" name="radio" /> <label for="radio1">Radio Button</label>
input[type='radio'] { display: none; /* we hide the default radio button and style the label */ } input[type='radio'] + label { padding: 10px 20px; background-color: #087f5b; color: white; border-radius: 5px; } input[type='radio']:checked + label { background-color: #0a9172; }
- You will notice that the
input
is hidden and thelabel
is styled to look like a button. When theinput
is checked, thelabel
changes color. (input
must exist in the same container as thelabel
but can be hidden)
- You will notice that the
Some web page authors split up their CSS style rules into separate style sheets. For example, they might use one style sheet to control the layout and another to control fonts, colors and so on.
-
There are two ways to add multiple style sheets to a page:
-
Your HTML page can link to one style sheet and that stylesheet can use the
@import
rule to import other style sheets.@import url('tables.css'); @import url('typography.css'); body { color: #666666; background-color: #f8f8f8; text-align: center; } #page { width: 600px; text-align: left; margin-left: auto; margin-right: auto; border: 1px solid #d6d6d6; padding: 20px; }
- If a styesheet uses the
@import
rule, it should appear before the other rules.
- If a styesheet uses the
-
In the HTML you can use a separate
<link>
element for each style sheet.<head> <title>Multiple Style Sheets - Link</title> <link rel="stylesheet" type="text/css" href="css/site.css" /> <link rel="stylesheet" type="text/css" href="css/tables.css" /> <link rel="stylesheet" type="text/css" href="css/typography.css" /> </head>
-
We can use layers to control the order in which styles are applied to the page. The last layer will override the previous layers.
- The
@layer
rule is a new feature in CSS that allows you to group related styles together in a single layer. - The
@layer
rule is used to define a new layer in a CSS file.
@layer base {
body {
color: #666666;
background-color: #f8f8f8;
text-align: center;
}
#page {
width: 600px;
text-align: left;
margin-left: auto;
margin-right: auto;
border: 1px solid #d6d6d6;
padding: 20px;
}
}
@layer components {
.button {
background-color: #087f5b;
color: white;
padding: 10px 20px;
border-radius: 5px;
}
@media (max-width: 600px) {
.button {
padding: 5px 10px;
}
}
}
@layer utilities {
.text-center {
text-align: center;
}
}
They're a way for browser makers to add support for new CSS features before those features are fully supported in all browsers. This may be done during a sort of testing and experimentation period where the browser manufacturer is determining exactly how these new CSS features will be implemented. These prefixes became very popular with the rise of CSS3.
Browser vendors used to add prefixes to experimental or nonstandard CSS properties and JavaScript APIs, so developers could experiment with new ideas. This, in theory, helped to prevent their experiments from being relied upon and then breaking web developers' code during the standardization process.
- You can check for the browser support from caniuse.com
-
The most common browser CSS prefixes you will see in older code bases include:
-moz-
(firefox)-o-
(old pre-WebKit versions of Opera)-ms-
(Internet Explorer and Microsoft Edge)
What is
WebKit
anyway?It's a browser rendering engine used by Safari, Chrome, and other browsers. It's known for its speed and efficiency.
- This means that just about every major browser has its roots in the WebKit codebase. This history helps explain why
-webkit
prefixes are supported in many non-Safari browsers!Nowdays,
WebKit
is used as a prefix for Safari and Chrome browsers, andBlink
is the new rendering engine for Chrome.
-
When adding them, make sure to add the standard property at the end, so that the standard property will override the vendor-prefixed properties.
-
Example:
div { -webkit-transition: all 4s ease; -moz-transition: all 4s ease; -ms-transition: all 4s ease; -o-transition: all 4s ease; transition: all 4s ease; /* This will make the transition work in all browsers */ }
-
Note: Nowadays, you don't need to add prefixes manually, because any CSS Preprocessor or Module Bundler will do it for you. (e.g.
Autoprefixer
,PostCSS
) -
you can check what prefixes you need to apply for certain properties from:
-
You can auto prefix your styles using module-bundler or an extension like:
It's the practice of building your web functionality so that it provides a certain level of user experience in more modern browsers, but it will also degrade gracefully to a lower level of user in experience in older browsers.
-
It's done using
@supports
rule to check if the browser supports a certain property or not -> it's called "Feature Query" -
It's used instead of vendor prefixes when we need to handle the case when the browser doesn't support a certain property (because the design will be broken)
-
Example
@supports (display: grid) { /* CSS rules for browsers that support grid layout */ .wrapper { display: grid; grid-template-columns: 1fr 1fr 1fr; } } @supports not (display: grid) { /* CSS rules for browsers that do not support grid layout */ .wrapper { display: flex; flex-wrap: wrap; } }
Display | Visibility |
---|---|
It specifies the display behavior of an element. | It specifies whether an element is visible or hidden. |
It's a CSS property that defines the type of box used for an HTML element. | It's a CSS property that determines whether an element is visible or hidden. |
It can have values like block , inline , inline-block , flex , grid , etc. |
It can have values like visible , hidden , collapse , initial , inherit . |
-
(
visibility:hidden
oropacity: 0
) => hide the element but preserves it's place (leaves a space where the element would have been) -
display: none
=> remove element from the flow, hide element and collapse its space-
note => it doesn't work for
animation/transition
(for Javascript usually), instead use (opacity
orvisibility
):/* ALL THESE FOR mimicking [display:none;] */ /* 1) Hide it visually and preserve the space */ opacity: 0; /* 2) Make it unaccessible to mouse and keyboard */ pointer-events: none; /* 3) Hide it from screen readers */ visibility: hidden; /* or */ opacity: 0;
-
-
display: block
--> can be used to make<a>
element take full width of its container -> for user clicking accessibility -
inline-block:
display: inline-block
--> causes a block-level element to flow like an inline element, while retaining other features of a block-level element.- doesn't start a new line (like inline element)
- respects
margin
,width
,height
(like block element)
To center an element vertically and horizontally in a container, we have these options:
-
If the element is
inline
, you can usetext-align: center
on the parent element.container { text-align: center; } .element { display: inline; }
- This will center the element horizontally in the container
- To center it vertically, you can use
vertical-align: middle
on the element if it's
-
Using
margin: auto
:.container { width: 700px; margin: 0 auto; } /* This will center the container horizontally on the page */
-
using
position: absolute
:.container { position: relative; } .element { width: 100px; height: 100px; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); /* or (NOT RECOMMENDED ❌) */ top: calc(50% - 50px); /* 50px is half of the element height */ left: calc(50% - 50px); /* 50px is half of the element width */ }
-
using
flexbox
:.container { display: flex; min-height: 100vh; /* to make the container full height */ flex-direction: column; justify-content: center; align-items: center; }
-
using
grid
:
There's a difference between text-align: center
and centering using flexbox or grid:
<!-- Using text-align -->
<div class="with-text-align">
<p>
It is advisable to drain golfing land much more thoroughly and efficiently than ordinary farm
land, but, on the other hand, by exercising a little thought it can be done much more cheaply.
For the purpose of golf it is not only unnecessary to drain as deeply as is customary for
agricultural purposes, but it is much cheaper and more satisfactory to adopt a system of shallow
drains.
</p>
</div>
<hr />
<!-- Using a Flex column -->
<div class="with-flexbox">
<p>
It is advisable to drain golfing land much more thoroughly and efficiently than ordinary farm
land, but, on the other hand, by exercising a little thought it can be done much more cheaply.
For the purpose of golf it is not only unnecessary to drain as deeply as is customary for
agricultural purposes, but it is much cheaper and more satisfactory to adopt a system of shallow
drains.
</p>
</div>
<style>
.with-text-align {
text-align: center;
}
.with-flexbox {
display: flex;
flex-direction: column;
align-items: center;
}
p {
max-width: 50ch;
padding: 1rem;
}
</style>
- What's going on here?
- Well,
text-align: center
moves all of the individual characters to the middle of each line, the way you'd expect centering to work in a rich text editor. align-items
, though, is more about layout alignment. It positions the paragraph as a block. It doesn't affect the individual characters within that block.
- Well,
To have smooth scrolling when clicking on links, you can use: css or javascript
-
This doesn't work on older versions of safari Browser
-
It's a simple way to add smooth scrolling to your page
-
It's done by adding
scroll-behavior: smooth;
to thehtml
elementhtml { scroll-behavior: smooth; }
-
This works on all browsers including older versions of safari Browser
-
It's done by adding an event listener to the links and then using the
scrollIntoView
method to scroll to the sectionconst allLinks = document.querySelectorAll('a:link'); allLinks.forEach(function (link) { link.addEventListener('click', function (e) { // 1. prevent default behavior e.preventDefault(); // 2. get the href attribute value const href = link.getAttribute('href'); // 3. Handle the scroll behavior if (href === '#') // Scroll back to top window.scrollTo({ top: 0, behavior: 'smooth' }); if (href !== '#' && href.startsWith('#')) { // Scroll to other links (sections with id attribute equal to the href value) const sectionEl = document.querySelector(href); sectionEl.scrollIntoView({ behavior: 'smooth' }); } }); });
-
Another way to do it is by using a polyfill for older browsers
<script defer src="https://unpkg.com/smoothscroll-polyfill@0.4.4/dist/smoothscroll.min.js"></script>
- then in JS file, write the same code as above
:red {
--base-size: 1rem;
--scale: 1.25;
--h5: calc(var(--base-size) * var(--scale)); /* 1.25rem */
--h4: calc(var(--h5) * var(--scale)); /* 1.5rem */
--h3: calc(var(--h4) * var(--scale)); /* 2rem */
--h2: calc(var(--h3) * var(--scale)); /* 2.5rem */
--h1: calc(var(--h2) * var(--scale)); /* 3rem */
--small: calc(var(--base-size) / var(--scale)); /* 0.8rem */
}
/* Usage */
h1 {
font-size: var(--h1);
}
Double borders on just the corner of some boxes may seem impossible to start with. However, there are several possible solutions to this issue.
-
The idea here is to create a double border effect by using a
::before
and::after
pseudo-elements both for the container and the first element inside it. -
Then we use
border-width
to control which sides you want to show based on what corner you want to apply the effect to. -
.container { position: relative; border: 2px solid #000; } .container::before, .container::after, .container > :first-child::before, .container > :first-child::after { content: ''; position: absolute; width: 40px; height: 40px; border-color: #000; border-style: solid; } /* now for each box, display it in its location and display the border on only 2 sides */ .container::before { top: 0; left: 0; border-width: 2px 0 0 2px; } .container::after { top: 0; right: 0; border-width: 2px 2px 0 0; } .container > :first-child::before { bottom: 0; left: 0; border-width: 0 0 2px 2px; } .container > :first-child::after { bottom: 0; right: 0; border-width: 0 2px 2px 0; }
-
when using 3d animation / transform => use
perspective
property on the parent element.card { perspective: 1000px; /* 3d effect */ } .card:hover { transform: rotateY(180deg); }
- it's used to give the 3d effect to the child elements (the lower the value, the more the 3d effect)
- If the user is right next to the screen, small changes in position will appear huge. Imagine spinning a card that's only a few inches from your face. If the user is further away, though, that same motion will appear smaller and more subtle.
-
when rotating cards 180 degrees and you want not the back of the card to be visible, we use the
backface-visibility
property:.card { backface-visibility: hidden; } .card:hover { transform: rotateY(180deg); }
-
Example
<style> .card-wrapper { perspective: 500px; /* 3d effect */ } .card { position: relative; display: block; } .front, .back { width: 200px; height: 200px; background: white; border-radius: 8px; display: flex; justify-content: center; align-items: center; transition: transform 400ms; will-change: transform; backface-visibility: hidden; /* To hide the back of the card */ /* Vendor prefix for Safari */ -webkit-backface-visibility: hidden; } .back { position: absolute; /* To make it appear behind the front */ top: 0; left: 0; transform: rotateY( -180deg ); /* To make it appear behind the front as it will be hidden because of backface-visibility */ } .card:hover .front, .card:focus .front { transform: rotateY(180deg); } .card:hover .back, .card:focus .back { transform: rotateY(0deg); } </style> <div class="card-wrapper"> <a href="/" class="card"> <div class="front"> <p>Hello World</p> </div> <div class="back"> <p>This is the back</p> </div> </a> </div>
-
Another example: Folding the DOM
- The trick here is to use
clip-path
property to create a circle that grows from the center of the button to cover the whole screen
.nav-bar {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: white;
display: flex;
justify-content: space-around;
align-items: center;
z-index: 1;
opacity: 1;
clip-path: circle(50px at 100% -10%); /* Masking the content of the nav-bar to be a circle */
-webkit-clip-path: circle(50px at 100% -10%);
}
-
The idea here is to create a floating cursor that follows the mouse movement on the page
-
It's just a simple div with a background color that follows the mouse movement using
mousemove
event -
Floating Cursor when moving the mouse
<!-- Down in the end of the body --> <div class="cursor"></div>
-
Approach 1: using absolute positioning
.cursor { width: 3rem; height: 3rem; border: 2px solid white; border-radius: 50%; position: absolute; transform: translate(-50%, -50%); pointer-events: none; transition: all 0.5s ease-in-out; transition-property: background, transform; transform-origin: 75% 75%; }
function cursor(e) { let mouse = document.querySelector('.cursor'); mouse.style.top = e.pageY + 'px'; mouse.style.left = e.pageX + 'px'; } window.addEventListener('mousemove', cursor);
-
Approach 2: using (fixed positioning &
transform
property).cursor { position: fixed; top: 0; left: 0; width: 30px; height: 30px; opacity: 1; background: rgba(0, 0, 0, 0.2); border: 1px solid transparent; transition: width 0.3s cubic-bezier(0.3, 0, 0.3, 1), height 0.3s cubic-bezier(0.3, 0, 0.3, 1), background 0.3s cubic-bezier(0.3, 0, 0.3, 1), margin 0.3s cubic-bezier(0.3, 0, 0.3, 1), opacity 0.7s cubic-bezier(0.3, 0, 0.3, 1); -webkit-transition: width 0.3s cubic-bezier(0.3, 0, 0.3, 1), height 0.3s cubic-bezier(0.3, 0, 0.3, 1), background 0.3s cubic-bezier(0.3, 0, 0.3, 1), margin 0.3s cubic-bezier(0.3, 0, 0.3, 1), opacity 0.7s cubic-bezier(0.3, 0, 0.3, 1); z-index: 999; pointer-events: none; border-radius: 100%; -moz-border-radius: 100%; -webkit-border-radius: 100%; -khtml-border-radius: 100%; }
function cursor(e) { let mouse = document.querySelector('.cursor'); mouse.style.transform = `translate(${e.pageX}px, ${e.pageY}px)`; } window.addEventListener('mousemove', cursor);
-
-
Growing Cursor when hovering over an element
- The idea here is to create a circle that grows from the center of the button to cover the whole button
.cursor { width: 3rem; height: 3rem; border: 2px solid white; border-radius: 50%; position: absolute; transform: translate(-50%, -50%); pointer-events: none; transition: all 0.5s ease-in-out; transition-property: background, transform; transform-origin: 75% 75%; } .cursor.nav-active { background: rgb(86, 124, 228); transform: scale(3); } .cursor.explore-active { background: white; transform: scale(3); } .cursor-text { position: absolute; top: 50%; left: 50%; font-size: 0.5rem; transform: translate(-50%, -50%); }
let mouse = document.querySelector('.cursor'); let mouseTxt = mouse.querySelector('span'); function activeCursor(e) { const item = e.target; // If the item is a link, the cursor will be a pointer if (item.id === 'logo' || item.classList.contains('burger')) { mouse.classList.add('nav-active'); } else { mouse.classList.remove('nav-active'); } // If the item is an explore link, the cursor will be a circle if (item.classList.contains('explore')) { mouse.classList.add('explore-active'); mouseTxt.innerText = 'Tap'; gsap.to('.title-swipe', 1, { y: '0%' }); } else { mouse.classList.remove('explore-active'); mouseTxt.innerText = ''; gsap.to('.title-swipe', 1, { y: '100%' }); } } // Event Listeners window.addEventListener('mouseover', activeCursor);
-
Notes:
- It's important to set
pointer-events: none;
to prevent the cursor from blocking the mouse events on the page (like clicking on items)
- It's important to set
- The idea here is to create a chat interface with messages that have different styles based on the sender or the receiver
- Also we can move the messages container to the bottom of the page so that it feels like a real chat interface
<style>
ol {
display: flex;
flex-direction: column;
justify-content: flex-end;
}
.message.sent {
align-self: flex-end;
}
</style>
<body>
<ol>
<li class="message sent">Can you get me a big salad?</li>
<li class="message received">What big salad? I'm going to the coffee shop.</li>
</ol>
</body>
On their desktop application, Facebook has a 3-column layout. There're some added borders so we can see how the columns flex:
-
It's a 3 column layout, and below a certain threshold, the left-hand navigation disappears. It's interesting how things scale, though!
-
When shrinking the screen below
1100px
:- the left-hand navigation disappears
- the right-hand sidebar starts to shrink before reaching a minimum width
- the center column appears to not shrink at all, as if it has more priority than the right-hand sidebar (until the right-hand sidebar reaches its minimum width, then they both shrink together)
-
Solution:
<style> .wrapper { display: flex; } nav, aside.contacts { min-width: 150px; max-width: 250px; flex-shrink: 9999999; /* This will make the element shrink more than the other elements */ flex-basis: 250px; } main { flex: 1; flex-basis: 500px; /* Here it doesn't have `flex-shrink` property, meaning it has the default value of 1, so it will shrink 1 px for each 9999999 px shrunk by the other elements */ } </style> <div class="wrapper"> <nav></nav> <main></main> <aside class="contacts"></aside> </div>
It's a common pattern to use a spacer component to add space between elements. This is especially useful when you want to add space between elements that are not direct siblings outside of the selector's styles to prevent overriding other styles.
/* Spacer Component */
const Spacer = ({ size }) => {
return <div style={{ height: size }} />;
};
/* Usage */
<div>
<h1>Heading</h1>
<Spacer size='20px' />
<p>Paragraph</p>
</div>;
The above is a vertical-spacer component. You can also create a horizontal-spacer component by changing the height
to width
.
-
The idea here is to create a scrollable part of a container that has a fixed height and a scrollbar and when we scroll (as the part B is overflowing), the part A will stay fixed and visible
-
Starter code
<style> section { display: flex; gap: 32px; border: 3px solid hotpink; } .col { flex: 1; padding: 16px; } </style> <section> <div class="col"> <h1>Growing Column</h1> <p> This column will grow very tall indeed, whilst the adjacent one will be clamped to whatever height this one rests at! </p> <p> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. </p> </div> <div class="col"> <p>Here is a list of all the letters in the English language:</p> <ol> <li>Item A</li> <li>Item B</li> <li>Item C</li> <li>Item D</li> <li>Item E</li> <li>Item F</li> <li>Item G</li> <li>Item H</li> <li>Item I</li> <li>Item J</li> <li>Item K</li> <li>Item L</li> <li>Item M</li> <li>Item N</li> <li>Item O</li> <li>Item P</li> <li>Item Q</li> <li>Item R</li> <li>Item S</li> <li>Item T</li> <li>Item U</li> <li>Item V</li> <li>Item W</li> <li>Item X</li> <li>Item Y</li> <li>Item Z</li> </ol> </div> </section>
-
Expected output & constraints:
- Two equal width columns
- The container should be the height of the first column.
- The second column should scroll vertically, since it won't fit in the shorter container.
-
Solution:
section { display: flex; gap: 32px; border: 3px solid hotpink; overflow: hidden; /* new ✅ */ } .col:first-of-type { position: sticky; /* new ✅ */ top: 0; /* new ✅ */ } .col:last-of-type { height: 0; /* new ✅ */ /* This will make the second column scrollable */ }
When you have a scrollbar on the page, it can affect the layout of the page or cover some of the content. You can track the scrollbar width using JavaScript and then adjust the layout accordingly.
const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
// window.innerWidth --> width of the window including the scrollbar
// document.documentElement.clientWidth --> width of the document excluding the scrollbar
-
Here's an example: Let's say our window is
500px
wide, and our scrollbar is taking up20px
.window.innerWidth
will be500
, andclientWidth
will be480
. By subtracting, we get the difference of20
. If there is no scrollbar, or the scrollbar is a non-blocking overlay,scrollbarWidth
will be0
, since our two width values will be identical.- Now that we know how wide the scrollbar is, we can assign it to a CSS variable:
document.documentElement.style.setProperty('--scrollbar-width', scrollbarWidth + 'px'); // By attaching this CSS variable to the documentElement, we make it globally available (it's the same thing as targeting html in CSS). // Now, in our CSS, we can use this variable to adjust our layout:
.wrapper { width: calc(100% - var(--scrollbar-width)); }
It's a technique that is used for some designs where you need to have an element centered in the middle of the container that has 2 elements and we want the design to be like if the container has 3 elements.
-
The idea here is to create a filler element that takes up the remaining space in the container and pushes the centered element to the middle of the container. This way the element that we want to center will be squished to the middle of the container by its left from the filler element and by its right from the other element.
-
The idea here that we need to apply
flex: 1;
to the filler element and the other elements will take the space they need. -
Example:
<div class="overlay"> <div class="container"> <div class="filler"></div> <div class="nav"></div> <a href="#">Sale</a> <a href="#">New Releases</a> <a href="#">Men</a> <a href="#">Women</a> <a href="#">Kids</a> <a href="#">Collections</a> </div> <div class="footer"> <a href="/terms">Terms and Conditions</a> <a href="/privacy">Privacy Policy</a> <a href="/contact">Contact Us</a> </div> </div> </div> <style> .overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); display: flex; justify-content: flex-end; } .container { width: 300px; height: 100%; background-color: white; display: flex; flex-direction: column; } .nav { display: flex; flex-direction: column; gap: 16px; } .filler { flex: 1; } .footer { display: flex; flex-direction: column; justify-content: flex-end; flex: 1; }
<main class="wrapper">
<h2>Chapter 1</h2>
<p>
Outside the space-warp chamber, Rizal's great green sun had already set. Thick olive dusk eddied
through the interplanetary transit center. I swore under my breath and slammed shut the
warp-hatch switch.
</p>
<p>
Locking bars whispered back. The hatch revolved on its axis, slow as an asteroid eroding. I
threw another quick glance at my chrono. It still read the same as before: six Earth hours more…
six hours to ferret out the truth or be forever reconditioned.
</p>
<p>—Six hours, that is, if Controller Alfred Kruze didn't cut it shorter.</p>
<p>
And if he did, Rizal might very well change status. Today, it was billed as the FedGov's
outermost bastion against the Kel. Tomorrow, it could prove man's fatal flaw, the Achilles heel
in our whole system of defenses.
</p>
<p>In which case—</p>
<p>Involuntarily, I shivered.</p>
<p>“Agent Traynor—?”</p>
<p>
The voice came from the shadows. A dull, phlegmatic, tranquilized, conditioned voice. I stopped
short; turned fast. “Who's asking?”
</p>
<p>
The man shrugged stolidly, not even picking up my tension. “I'm a port rep, Agent Traynor. Port
rep second, that is—”
</p>
<p>“So who told you to come out here? Who said you should meet me?”</p>
<p>
“Oh…” A pause. “Well, you see, there's this sigman, Agent Traynor. Up in the Interworld
Communications section. He had a regular 7-D clearance report that a FedGov Security
investigation agent was warping in—you have to file a 7-D on all warpings, you know, Agent
Traynor, on account of restrictives. So—well, the rep first was out to eat, so I just notified
Rizal Security, just a routine report, and the unit controller there, an Agent Gaylord, he said
for me to meet you, and—”
</p>
<p>
I bit down hard and shifted my weight, both at once, wondering if a broken jaw would interfere
with the work of a port rep second.
</p>
<p>Only then, all at once, I caught the unmistakable whish of a grav-car sweeping in.</p>
<p>
The lights hit us almost in the same instant. Two seconds later a man who said he was Agent
Gaylord was jumping down and locking wrists with me in Rizal's traditional greeting.
</p>
<p>
Even that wrist-lock set my teeth on edge. It was too solid, too stolid, too thorough a job of
conditioning.
</p>
<p>Or was it maybe, just a trifle over-done?</p>
</main>
.wrapper {
column-count: 2;
column-gap: 150px;
max-width: 64rem;
margin: 32px auto;
border: 2px solid hsl(35deg 10% 40%);
padding: 50px;
background: linear-gradient(
to right,
hsl(35deg, 30%, 90%),
hsl(35deg, 30%, 90%) 47%,
hsl(35deg, 30%, 70%) 49.5%,
hsl(35deg, 20%, 50%) 50%,
hsl(35deg, 30%, 70%) 50.5%,
hsl(35deg, 30%, 90%) 53%,
hsl(35deg, 30%, 90%)
);
}
h2 {
font-size: 2rem;
margin-bottom: 2em;
}
p {
text-align: justify;
}
p:first-of-type:first-letter {
font-size: 3em;
float: left;
line-height: 1em;
margin-right: 0.2em;
}
p:not(:first-of-type) {
text-indent: 2em;
}
* {
font-family: 'Merriweather', serif;
}
-
"stairs" layout using CSS Grid:
<div class="wrapper"> <div class="box one"></div> <div class="box two"></div> <div class="box three"></div> </div> <style> .wrapper { display: grid; grid-template-rows: repeat(3, 1fr); min-height: 100%; justify-items: center; /* center the items horizontally on the grid row */ } .box { width: 50%; } .box.one { background-color: pink; justify-self: end; /* align the item to the end of the grid row */ } .box.two { background-color: lavender; } .box.three { background-color: peachpuff; justify-self: start; /* align the item to the start of the grid row */ } html, body { height: 100%; } </style>
-
- The idea is that all rectangles are the same size, but they are broken in the middle to create a unique layout.
- You might think that you need to use a lot of grid columns and rows to achieve this layout, but you can do it with just one row and 2 columns (one for the left side and one for the right side).
- We also need to store the desired full width of the shape in a CSS variable (
--rect-width
)
<div class="wrapper"> <div class="box one"></div> <div class="box two"></div> <div class="box three"></div> <div class="box four"></div> <div class="box five"></div> <div class="box six"></div> </div>
-
.wrapper { --rect-width: 100px; display: grid; grid-template-columns: repeat(2, 1fr); min-height: 100%; } .box { width: var(--rect-width); height: 80px; } .box.one { background-color: pink; } .box.two { background-color: pink; } .box.three { background-color: lavender; } .box.four { background-color: lavender; } .box.five { background-color: honeydew; } .box.six { background-color: honeydew; } html, body { height: 100%; }
-
phase 2: move the odd boxes to the end of the grid row, then add a gap between the boxes, and finally, adjust the width of the boxes to create the broken rectangles
.wrapper { --rect-width: 100px; display: grid; grid-template-columns: repeat(2, 1fr); align-content: center; /* center the items vertically on the grid row */ gap: 4px; /* add a gap between the grid items */ min-height: 100%; } .box:nth-of-type(odd) { justify-self: end; /* align the odd items to the end of the grid row */ } .box { width: var(--rect-width); height: 80px; } .box.one { background-color: pink; width: calc(var(--rect-width) * 0.25); /* adjust the width of the first box */ } .box.two { background-color: pink; width: calc(var(--rect-width) * 0.75); /* adjust the width of the second box */ } .box.three { background-color: lavender; width: calc(var(--rect-width) * 0.5); /* adjust the width of the third box */ } .box.four { background-color: lavender; width: calc(var(--rect-width) * 0.5); /* adjust the width of the fourth box */ } .box.five { background-color: honeydew; width: calc(var(--rect-width) * 0.75); /* adjust the width of the fifth box */ } .box.six { background-color: honeydew; width: calc(var(--rect-width) * 0.25); /* adjust the width of the sixth box */ } html, body { height: 100%; }
A common blog layout involves a single, centered column of text, with images that either span the full width of the column or are centered within it. This is known as a full-bleed layout.
-
Type 1: images are centered within the column
<style> .max-width-wrapper { max-width: 30ch; /* 30 characters wide, or whatever you prefer */ padding: 32px; margin-left: auto; margin-right: auto; } </style> <div class="max-width-wrapper"> <h1>Some Heading</h1> <p> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. </p> <img alt="a satisfied-looking cute meerkat" src="/course-materials/meerkat.jpg" class="meerkat" /> <p> It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. </p> </div>
- Using a
max-width
wrapper is a solid approach, but it does lock us in; every in-flow child will be constrained by that container. That's where type 2 comes in.
- Using a
-
Type 2: images span the full width of the column
Having an element stretch from edge to edge is known as a "full-bleed" element, a term borrowed from the publishing world when magazine ads would be printed right to the edge of the page.
- Fortunately, CSS Grid offers a very clever solution to this problem.
<style> .wrapper { display: grid; grid-template-columns: 1fr min(30ch, 100%) 1fr; } .wrapper > * { grid-column: 2; } .full-bleed { grid-column: 1 / -1; } .meerkat { display: block; width: 100%; height: 300px; object-fit: cover; } </style> <main class="wrapper"> <h1>Some Heading</h1> <p> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. </p> <div class="full-bleed"> <img alt="a satisfied-looking cute meerkat" src="/course-materials/meerkat.jpg" class="meerkat" /> </div> <p> It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. </p> </main>
-
- We have 3 explicit columns: the first and last are
1fr
wide, and the middle column ismin(30ch, 100%)
wide. - the
ch
unit is equal to the width of the 0 character, in the current font. Let's assume that in the current situation, our 0 character is15px
wide. This means that our30ch
value translates to450px
. 450px
is too wide to fit on many mobile displays. That's why we have that min() function. It clamps this value so that it never grows above 100% of the available space. On a375px
-wide phone, our center column will be375px
wide, not 450px.- Our two side columns will share whatever space remains. Like auto margins, this is a clever way to make sure the middle column is centered.
- We have 3 explicit columns: the first and last are
-
-
As we start adding children to this grid, they'll be assigned into the first available cell. This doesn't work for us: we want all of our content to be assigned to that middle column by default, That's where this CSS comes in:
.wrapper > * { grid-column: 2; /* every direct child of .wrapper will be assigned to the second column */ }
-
-
-
The
full-bleed
class is a simple way to make an element span the full width of the grid. It's a common pattern in CSS Grid to use a class like this to target full-bleed elements..full-bleed { grid-column: 1 / -1; /* span from the first column to the last column */ }
-
This can lead to some very tall images, on very wide screens, so it's better to combine it with a fixed
height
andobject-fit
:.meerkat { display: block; width: 100%; height: 300px; object-fit: cover; }
-
-
Adding gutters (for small screens)
-
We can add
padding
to create some space between the text and the image. This is a great way to add some breathing room to the layout..wrapper { display: grid; grid-template-columns: 1fr min(30ch, 100%) 1fr; padding: 0 16px; /* add padding to the grid container */ }
-
Note: this will create a problem for the "full-bleed" element, as it will not be full-bleed anymore. To fix this, we need to use "negative margin" to make the full-bleed element span the full width of the grid container:
.full-bleed { grid-column: 1 / -1; /* span from the first column to the last column */ margin-left: -16px; /* add negative margin to the left */ margin-right: -16px; /* add negative margin to the right */ }
- So, Our container has 16px of padding, but our full-bleed children will undo that, using the negative margin trick
-
import React from 'react';
import styled from 'styled-components/macro';
import { MARKET_DATA, SPORTS_STORIES } from '../../data';
import { QUERIES } from '../../constants';
import MarketCard from '../MarketCard';
import SectionTitle from '../SectionTitle';
import MiniStory from '../MiniStory';
const SpecialtyStoryGrid = () => {
return (
<Wrapper>
<MarketsSection>
<SectionTitle
cornerLink={{
href: '/markets',
content: 'Visit Markets data »'
}}>
Markets
</SectionTitle>
<MarketCards>
{MARKET_DATA.map(data => (
<MarketCard key={data.tickerSymbol} {...data} />
))}
</MarketCards>
</MarketsSection>
<SportsSection>
<SectionTitle
cornerLink={{
href: '/sports',
content: 'Visit Sports page »'
}}>
Sports
</SectionTitle>
<SportsStories>
{SPORTS_STORIES.map(data => (
<SportsStoryWrapper key={data.id}>
<MiniStory {...data} />
</SportsStoryWrapper>
))}
</SportsStories>
</SportsSection>
</Wrapper>
);
};
const Wrapper = styled.div`
display: grid;
gap: 48px;
@media ${QUERIES.tabletAndUp} {
gap: 64px;
grid-template-columns: minmax(0px, auto);
}
@media ${QUERIES.laptopAndUp} {
gap: 0px;
grid-template-columns: 1fr minmax(0px, 1fr);
}
`;
const MarketsSection = styled.section`
@media ${QUERIES.laptopAndUp} {
padding-right: 16px;
margin-right: 16px;
border-right: 1px solid var(--color-gray-300);
}
`;
const MarketCards = styled.div`
display: grid;
grid-template-columns: repeat(auto-fill, minmax(165px, 1fr));
gap: 16px;
`;
const SportsSection = styled.section``;
const SportsStories = styled.div`
display: grid;
grid-template-columns: repeat(auto-fill, minmax(165px, 1fr));
gap: 16px;
@media ${QUERIES.tabletAndUp} {
display: flex;
grid-template-columns: revert;
overflow: auto;
}
`;
const SportsStoryWrapper = styled.div`
min-width: 220px;
`;
export default SpecialtyStoryGrid;
- Here, the trick is using
auto-fill
in thegrid-template-columns
property to create a flexible grid that will automatically add as many columns as it can fit in the container. This is a great way to create a responsive grid that will adapt to the available space. - Also, Handling overflow on the
SportsStories
container is a great way to make sure that the grid will be scrollable on smaller screens. This is a common pattern for responsive grids that need to adapt to different screen sizes.
-
To create a container for the whole page, we use
<div>
element withid
attribute and give it a name likecontainer
orwrapper
and then we put all the page content inside it. and to give the sections inside it amax-width
and center them, we use:-
page's container width:
.container { width: 100%; max-width: 120rem; }
-
centering the sections inside it:
/* OPTION 1 */ .section { width: 100%; margin: 0 auto; } /* OPTION 2 */ body { display: flex; flex-direction: column; justify-content: center; }
-
-
to center an inline element on the page:
img.align-center { text-align: center; } /* or */ img.align-center { display: block; margin: 0px auto; }
-
when creating base styles (resets), you may also want to apply it to
::before
and::after
pseudo-elementshtml { box-sizing: border-box; font-size: 100%; } *, *::before, *::after { box-sizing: inherit; }
-
usually when using compiler and bundling multiple
css
/sass
files into one.css
file, you may see file with extension:.css.map
- this file is for mapping the style rules to their files so that it would show in the devtools to ease the debugging process
-
inline elements (
<span>
) are sensitive to spaces (before & after it) unlike other elements, To avoid this you can:-
remove spacing between elements manually by making it very close to its siblings/ancestors
<p> <span> Foo </span><span> Bar </span> </p> <ul> <li><a href="#">Home</a></li><li><a href="#">About</a></li><li><a href="#">Contact</a></li> </ul>
-
Set
zero
Font Size on Parent Elementul { padding: 0; list-style: none; font-size: 0; } ul li { font-size: 16px; display: inline-block; background: orange; padding: 5px 20px; }
-
-
To add custom underline to element:
-
use
border-bottom
property instead oftext-decoration
property to have more control over the underline.- usually to control the space between the text and the underline, use
padding-bottom
property.
- usually to control the space between the text and the underline, use
-
Or: use
::after
pseudo-element withcontent
property to add the underline.- this option is better if you want to have more control over the underline like:
color
,width
,z-index
,position
,border-radius
, etc.
.underline::after { content: ''; display: block; width: 100%; height: 2px; background-color: #000; }
- this option is better if you want to have more control over the underline like:
-
-
when you have a sliding item (animate from right to center like a sidebar) => use this :
body, html { overflow-x: hidden; }
-
The
"/"
character is becoming a more common pattern in modern CSS. It isn't about division, it's about separation. The slash allows us to create groups of values.rgba(0, 0, 0, 0.5)
=>rgb(0, 0, 0) / 0.5
, this is a shorthand for thergba
functionfont: 1rem/1.5 'Arial', sans-serif;
=>font-size: 1rem; line-height: 1.5; font-family: 'Arial', sans-serif;
border-radius: 10px / 20px;
=>border-top-left-radius: 10px; border-top-right-radius: 20px; border-bottom-right-radius: 10px; border-bottom-left-radius: 20px;
-
You can have custom bullet points for lists using
list-style-type
property:-
Approach 1:
ul { list-style-type: none; } li::before { content: '🔥'; margin-right: 10px; }
-
Approach 2:
ul { list-style-type: circle; list-style-type: '🔥'; /* or an image / icon */ list-style-image: url('icon.png'); }
-
Approach 3: (different type for each list item with
@counter-style
)
-
-
When styling
<a>
elements, it's recommended to style them with pseudo-classes in this order::link
-> for unvisited links withhref
attribute:hover
-> when the mouse is over the link:active
-> when the link is being clicked:visited
-> for visited links
a, a:link { color: #000; } a:visited { color: #666; } a:hover { color: #f00; } a:active { color: #0f0; }
-
If you want to create a line with round edges (like a pill shape), you can use the
border-radius
property with a value that is half of the height of the element..pill { height: 50px; border-radius: 25px; }
-
To create a diagonal line on an element, you can:
-
use
::before
or::after
pseudo-elements withcontent
property and rotate it usingtransform
property -
use
clip-path
property withpolygon
function -
use
linear-gradient
background.diagonal-line { background: linear-gradient(to top right, #fff calc(50% - 1px), black, #fff calc(50% + 1px)); /* or to be transparent */ background: linear-gradient( to top right, transparent calc(50% - 1px), black, transparent calc(50% + 1px) ); }
-
-
To have a video playing as a background of elements:
<section> <div class="content">Content</div> <div class="bg-video"> <video class="bg-video__content" autoplay muted loop> <source src="video.mp4" type="video/mp4" /> <source src="video.webm" type="video/webm" /> Your browser is not supported! </video> </div> </section>
section { position: relative; background-color: rgba(0, 0, 0, 0.5); } .bg-video { /* 1. make the video cover full container and positioned absolutely */ position: absolute; top: 0; left: 0; height: 100%; width: 100%; /* 2. make the video go behind the content */ z-index: -1; /* 3. make the video fit the container */ object-fit: cover; }
-
If you have multiple flex items that you want tem to have the same width (space in the container), you can use
flex: 1
for both of them.container { display: flex; } .item { flex: 1; }
-
Why Use flex: 1 Instead of Just flex-grow: 1?
flex: 1
is a shorthand forflex-grow: 1
,flex-shrink: 1
,flex-basis: 0
flex-grow: 1
will make the item grow to fill the container, but it won't make the items have the same width.- The Key Trick: Setting
flex-basis: 0
ensures that items have the same hypothetical width. This means the container will have a lot of free space since all items start with a width of0
. The free space is then distributed equally among the items becauseflex-grow: 1
is set for all of them.
-
Summary:
- What
flex: 1
does is that it resets all of the items' widths to0
and then makes them grow to fill the container equally.
- What
-
-
Scrollburglars
-
A scrollburglar is my cute made-up name for a common phenomenon: A webpage has an accidental horizontal scrollbar that allows you to scroll by a few pixels. (Usually happens on smaller screens (phones) or when the browser is not maximized)
-
Frustratingly, there isn't a single cause for this phenonenon. They can be triggered by lots of different things. Some examples:
- Elements with negative margins that pull content outside the viewport (usually with
!important
). - Absolute positioned elements that extend beyond their container's bounds, explicitly pulled outside of the parent (positioned elements with negative left/right values, elements with negative margin, etc).
- Images or other media that are wider than their container
- White space in HTML that gets interpreted as actual space, especially with inline-block elements
- An element has an explicit width that is too large to fit in the parent container.
- A really long word like
“disestablishmentarianism”
forces an element to be too wide for its parent container.
- Elements with negative margins that pull content outside the viewport (usually with
-
When trying to find which reason is causing the issue, you can use Divide and Conquer technique:
- Remove half of the page's content.
- Check if the issue is still there.
- If it is, remove half of the remaining content.
- If it isn't, add back half of the removed content.
- Repeat until you find the culprit.
-
-
When you find that you want to make cards have the same height, or each card to have just enough height to fit the content, you can use
align-items
property both on flexbox and grid layouts./* cards have the same height */ .container { display: flex; align-items: stretch; /* 👈 */ } /* each card has just enough height to fit the content */ .container { display: flex; align-items: flex-start; /* 👈 */ }
-
Wildcard (
*
) performance- You may have heard that using the wildcard selector (*) is bad practice. Some will tell you that it is slow, and can affect the overall performance of your page.
- Happily, this is not true. It's been debunked again and again. Even in 2009, when computers were slower and browsers were less optimized, this wasn't an issue. Performance concerns around the wildcard selector are a particularly-resilient urban legend.
-
What decides which styles are applied to an element?
- The Cascade and Specificity.
-
why is the
:hover
styles override the normal styles?- because by adding the
:hover
selector, you're increasing the specificity of the rule, so it will override the normal styles.
- because by adding the
-
If you can't use flexbox or grid, how would you make 2 elements be in 2 columns in the container and vertically centered?
- I would set the width for each row, then use:
display: inline-block;
for the elements andvertical-align: middle;
for them.- Or,
display: table
for the container anddisplay: table-cell;
for the elements, andvertical-align: middle;
for them. - Or, use
float: left;
for the elements andmargin: auto;
for them.
- I would set the width for each row, then use:
-
As a general rule, elements can't participate in multiple layout modes at once. Either it's using flexbox, or it's using positioned layout. This is ultimately a very good thing, because CSS would be much more complicated if this wasn't true!
- When there is a conflict between layout modes, positioned layout always wins.