Skip to main content

Intermediate CSS

I. Background Information

1. Default Styles

Resource

Browsers apply a set of default styles to every webpage. These styles are part of the user-agent stylesheets, ensuring basic styling for webpages without CSS. Each browser has its own set of user-agent stylesheets so the default styles can vary slightly between browsers.

  • The rules you write in your stylesheet have higher precedence than the user-agent rules and therefore overwrite the defaults.
  • Some developers use CSS resets, which are stylesheets containing CSS rules aimed at altering/removing the defaults set by user-agent stylesheets → create a clean slate to apply styles without interference. CSS resets are still commonly used, but they are not mandatory.

2. CSS Units

Resources

1. Absolute units

Absolute units are those that are always the same in any context. For web projects, px is the most commonly used absolute unit.

2. Relative units

Relative units are units that can change based on their context.

Even though em and rem are based on the font size, they can be used to define other sizes in CSS.

  1. em: is a unit based on the font size of the element (or the element’s parent).
  • When you set a font-size on a parent <div>, the em units for all child elements inside this <div> will be relative to that font-size.

→ Relying on em means that a particular size could change if the context changes, which is NOT the desired behavior.

Example: A parent <div> with a font-size set to 16px. If you set the width of a child element within that <div> to 2em, the child element's width will be 32px (= 16px x 2).

  1. rem: is also a unit based on the font size, but of the root element <html>. The math works the same with rem as it did with em but without the added complexity of keeping track of the parent’s font size.

→ Using a relative size like rem to define font sizes across your website is recommended.

3. Viewport units

The viewport of a page is the part of the web page that is visible within the web browser window. The viewport size can change depending on the device (desktop, tablet, mobile) and the browser window size.

The units vh and vw relate to the size of the viewport.

  • 1vh is equal to 1% of the viewport height.
  • 1vw is equal to 1% of the viewport width. → These can be useful any time you want something to be sized relative to the viewport, examples including full-height heroes, and full-screen app-like interfaces.

3. Pseudo-selectors

1. pseudo-classes

A pseudo-class is a selector that selects elements that are in a specific state. Pseudo-classes are represented by a single colon (:) followed by the pseudo-class name. Pseudo-classes share the same specificity as regular classes.

1. Dynamic and User Action
  • :hover: targets an element when the user hovers over it.
  • :focus: targets an element when the user focuses on that element by clicking or using keyboard controls.
  • :active: targets an element when it is activated (e.g., a link being clicked).
  • :link: targets unvisited links
  • :visited: targets visited links.
  • :checked: targets a checked form control element (e.g., a checkbox or radio button).
<a href="#">Visit this link!</a>
/* user action pseudo-class */
/* when a mouse hovers over the link */
a:hover {
color: green;
}

/* when the link is being clicked on */
a:active {
color: red;
}

/* when a link has been visited */
a:visited {
color: purple;
}
2. Structural
  • :root: targets the root element of the document.
  • :first-child: targets the first child element.
  • :last-child: targets the last child element.
  • :only-child: targets an element that is the only child.
  • :nth-child(<number>): targets one or more elements based on their source order
  • :empty: targets empty elements.
<div class="container">
<p>I'm the first paragraph.</p>
<p>I'm the second paragraph.</p>
<p>I'm the third paragraph.</p>
<p>I'm the fourth paragraph.</p>
</div>
/* structural pseudo-class */
/* we're using descendant combinators with pseudo-classes
-> there is space between .container and the pseudo-class name
We want to select elements inside the container, not the container itself ->
*/
.container :first-child {
color: red;
}

.container :last-child {
color: blue;
}

.container :nth-child(2) {
color: green;
}

.container :nth-child(3) {
color: orange;
}

2. pseudo-elements

A pseudo-element is represented by two colons (::) followed by the pseudo-element name. They are used to target elements that don’t normally exist in the markup and aren’t elements at all. These special elements share the same specificity as regular elements.

Examples of pseudo-elements:

  • ::marker: allows you to customize the styling of your <li> elements’ bullets or numbers.
  • ::before and ::after allow us to add extra elements onto the page with CSS, instead of HTML.
  • ::first-line,::first-letter: styles the first line of text in an element.
  • ::selection: allows you to change the highlighting when a user selects text on the page.
<div class="checklist">
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
</div>
/* PSEUDO ELEMENTS */
.checklist p::before {
content: "item ";
}

.checklist p::after {
content: " ✅"
}

.checklist p::selection {
color: yellowgreen;
}

.checklist p::first-letter {
text-transform: capitalize;
color: red;
}

4. Attribute Selectors

An attribute is anything in the opening tag of an HTML element - such as src='picture.jpg' or href="www.theodinproject.com", class=" and id=" are attributes. Attribute selectors target attributes and have the same specificity as classes and pseudo-classes.

  • [attribute]: select anything where the given attribute exists. Its value doesn’t matter.
  • selector[attribute]: combine our attribute selectors with other types of selectors, such as class or element selectors.
  • [attribute="value"]: to get really specific, we can use = to match a specific attribute with a specific value.
[src] {
/* This will target any element that has a src attribute. */
}

img[src] {
/* This will only target img elements that have a src attribute. */
}

img[src="puppy.jpg"] {
/* This will target img elements with a src attribute that is exactly "puppy.jpg" */
}

There is a syntax that allows matching a part of the attribute’s value:

  • [attribute^="value"] - ^= Will match strings from the start.
  • [attribute$="value"] - $= Will match strings from the end.
  • [attribute*="value"] - *= The wildcard selector will match anywhere inside the string
[class^='aus'] {
/* Classes are attributes too!
This will target any class that begins with 'aus':
class='austria'
class='australia'
*/
}

[src$='.jpg'] {
/* This will target any src attribute that ends in '.jpg':
src='puppy.jpg'
src='kitten.jpg'
*/
}

[id*='ill'] {
/* This will target any id attribute that has 'ill' anywhere inside it:
for="bill"
for="jill"
for="silly"
for="ill"
*/
}

II. CSS Properties

1. Fonts

Resources

1. Fonts

When you specify a font in CSS using the font-family property, providing a list of fallback fonts is a good practice. This list is known as a font stack. If the first font in the list is not available on the user's system, the browser will try the next font, and so on, until it finds an available font. If a default font isn’t defined, the default HTML font will be used.

/* example of the system's font stack */
body {
font-family: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}

2. Web Fonts

If you want to use a font that won’t be available on the user’s device, you will need to import the font from an online source.

  1. Online font libraries: To use a font from one of these libraries, go to the website, select a font, and then copy a snippet from the website to import that font from their server into your website.

You’ll be given either a <link> tag to put in your HTML or an @import tag that goes on top of a CSS file.

<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto&display=swap" rel="stylesheet">
@import url('https://fonts.googleapis.com/css2?family=Roboto&display=swap');

Note: When using a font library, you must comply with the library’s privacy policies and regulations. For example, using Google Fonts API violates the European GDPR. If you are concerned about complying with such regulations, you can download the font from the library and host it yourself.

  1. Self hosted fonts: In your CSS file, you import and define a custom font using the @font-face rule, and then use it as you would any other font-family.
@font-face {
font-family: my-cool-font;
src: url(../fonts/the-font-file.woff);
}

h1 {
font-family: my-cool-font, sans-serif;
}
css/

├── your-css-file.css
├── fonts/
│ └── the-font-file.woff

2. Text styles

This is a continuation of Typography in CSS Basics.

1. font-style

Typically used to make a font italic. The em tag also uses an italic font, but it also signifies that the wrapped text should be emphasized in some way. If you want the text to just be italic(or bold, underlined, highlighted, etc.), use a CSS property.

h1 {
font-style: italic;
}

We should use the em element if italics are required for emphasis.

<p>I <em>never</em> said he stole your money</p>

2. letter-spacing

letter-spacing changes the space between letters in a word.

<h1>Hello World!</h1>
<h1 class="wide">Hello World!</h1>
<h1 class="narrow">Hello World!</h1>
/* styles.css */
h1 {
font-family: 'COUTUREBold';
margin: 0;
font-size: 38px;
}

.wide {
font-size: 24px;
letter-spacing: .5em;
}

.narrow {
font-size: 48px;
letter-spacing: -.15em;
}

3. line-height

line-height adjusts the space between lines in wrapped text.

<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Ratione eius quibusdam ut, maiores quasi, iure architecto, necessitatibus sed culpa maxime distinctio ipsa nam repellat nostrum laboriosam tempora. Mollitia, pariatur velit!</p>
<p class="line-height">Lorem ipsum dolor sit, amet consectetur adipisicing elit. Reiciendis sequi est modi, temporibus eaque fugiat molestias obcaecati distinctio cupiditate. Beatae itaque eum eos deserunt illo consequatur quisquam similique dolorum adipisci.</p>
p.line-height {
line-height: 1.5;
}

4. text-transform

text-transform changes the case of the given text, taking in values such as capitalize, uppercase, lowercase, or none.

5. text-shadow

text-shadow adds a shadow around the text in the selected element.

/* offset-x | offset-y | blur-radius | color */
text-shadow: 1px 1px 2px black;

6. ellipsis

With the text-overflow property, you can truncate overflowing text with an ellipsis.

.overflowing {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

![](https://i.imgur.com/rg8cttD.png)

3. background

The background property is a shorthand for 8 different background-related properties. These 8 properties can be used individually, and in many cases, it’s easier and clearer to do that than defaulting to the shorthand.

1. background-image

This property sets one or more background images for an element. It can take one or more URL values, as well as the none value to remove any previously set background images.

This example sets two background images for the body element: image1.jpg and image2.png.

body {
background-image: url('image1.jpg'), url('image2.png');
}

2. background-attachment

This property sets whether a background image’s position is fixed within the viewport, or scrolls with its containing block. The possible values are:

  • scroll: The background scrolls with the content (default value).
  • fixed: The background is fixed relative to the viewport, so it doesn't scroll with the content.
  • local: The background is fixed relative to the element's content, so it scrolls with the element's content.

In this example, the background image will remain fixed in the viewport, even if the page content scrolls.

body {
background-image: url('image.jpg');
background-attachment: fixed;
}

3. background-color

This property sets the background color of an element. It can take any valid CSS color value, such as a named color, hexadecimal value, RGB value, or HSL value.

This example sets the background color of the body element to a light gray.

body {
background-color: #f2f2f2;
}

4. background-clip

This property specifies whether the background should extend into the padding, border, or content area of an element. The possible values are:

  • border-box: The background extends into the border area (default value).
  • padding-box: The background extends into the padding area.
  • content-box: The background is clipped to the content area.
  • You can combine two keyword values, e.g., top left, center right, etc.

In this example, the yellow background color will only be visible within the content area, excluding the padding and border.

div {
background-color: yellow;
background-clip: content-box;
padding: 20px;
border: 10px solid black;
}

5. background-origin

This property specifies the positioning area of the background images. The possible values are:

  • padding-box: The background is positioned relative to the padding area (default value).
  • border-box: The background is positioned relative to the border area.
  • content-box: The background is positioned relative to the content area.

In this example, the background image will be positioned relative to the content area, excluding the padding and border.

div {
background-image: url('image.jpg');
background-origin: content-box;
padding: 20px;
border: 10px solid black;
}

6. background-position

This property sets the initial position of the background image(s). It accepts various values, including keywords (top, right, bottom, left, center), length values, and percentages.

The background-position property is set to center left, which means the background image will be centered vertically but aligned to the left horizontally within the body element.

body {
background-image: url('image.jpg');
background-position: center left;
}

7. background-repeat

This property sets how the background image(s) should be repeated or tiled. The possible values are:

  • repeat: The background image is repeated both horizontally and vertically (default value).
  • repeat-x: The background image is repeated only horizontally.
  • repeat-y: The background image is repeated only vertically.
  • no-repeat: The background image is not repeated.

In this example, the pattern.png image will be repeated horizontally across the body element.

body {
background-image: url('pattern.png');
background-repeat: repeat-x;
}

8. background-size

This property specifies the size of the background image(s). It can take various value types, including keywords (cover, contain), length values, percentages, and multiple values for different dimensions.

This example sets the background image to cover the entire div element while maintaining its aspect ratio.

div {
background-image: url('image.jpg');
background-size: cover;
}

4. border

The border shorthand property allows you to set all three properties (border-width, border-style, and border-color) in a single declaration, making it more concise and easier to manage.

/* width | style | color */
border: medium dashed green;
border: 1px solid black;

1. border-width

This property sets the width of an element's border. It can take one, two, three, or four values, which represent the width of the top, right, bottom, and left borders respectively. Possible values:

  • thin (thin border)
  • medium (medium border)
  • thick (thick border)
  • A length value in px, em, rem, etc.
div {
border-width: 2px; /* All four borders are 2px wide */
border-width: 1px 3px; /* Top and bottom are 1px, right and left are 3px */
border-width: 2px 4px 6px; /* Top is 2px, left and right are 4px, bottom is 6px */
border-width: 1px 2px 3px 4px; /* Top is 1px, right is 2px, bottom is 3px, left is 4px */
}

2. border-style

This property sets the style of an element's border. It can take one, two, three, or four values, which represent the style of the top, right, bottom, and left borders respectively. Possible values:

  • none (no border)
  • hidden (hidden border)
  • solid (solid border)
  • dotted (dotted border)
  • dashed (dashed border)
  • double (double border)
  • groove (3D grooved border)
  • ridge (3D ridged border)
  • inset (3D inset border)
  • outset (3D outset border)
div {
border-style: solid; /* All four borders are solid */
border-style: dotted dashed; /* Top and bottom are dotted, right and left are dashed */
border-style: ridge inset outset groove; /* Top is ridge, right is inset, bottom is outset, left is groove */
}

3. border-color

This property sets the color of an element's border. It can take one, two, three, or four values, which represent the color of the top, right, bottom, and left borders respectively. Possible values:

  • Any valid CSS color value (hex, rgb, rgba, hsl, hsla, named colors, etc.)
div {
border-color: red; /* All four borders are red */
border-color: #0000ff blue; /* Top and bottom are #0000ff, right and left are blue */
border-color: rgb(0, 255, 0) rgba(255, 0, 0, 0.5) yellow; /* Top is green, right is semi-transparent red, bottom is yellow */
}

4. border-radius

This property allows you to create rounded corners on an element's border. It can be specified using one, two, three, or four values, which represent the radius of the top-left, top-right, bottom-right, and bottom-left corners, respectively. Possible values:

  • A length value in px, em, rem, etc.
  • A percentage value, which is calculated based on the size of the border box.
/* All four corners have a 10px radius */
div {
border-radius: 10px;
}

/* Top-left and bottom-right have a 10px radius, top-right and bottom-left have a 20px radius */
div {
border-radius: 10px 20px;
}

/* Top-left is 10px, top-right is 20px, bottom-right is 30px, bottom-left is 40px */
div {
border-radius: 10px 20px 30px 40px;
}

/* Top-left is 50%, top-right is 25%, bottom-right is 75%, bottom-left is 0% */
div {
border-radius: 50% 25% 75% 0%;
}

You can also use the shorthand properties border-top-left-radius, border-top-right-radius, border-bottom-right-radius, and border-bottom-left-radius to set the radius of individual corners.

5. box-shadow

This property is used to add shadow effects around an element's frame. It can create a variety of shadow effects, from subtle to dramatic, depending on the values you use.

  • If only two values are given, they are interpreted as <x-offset>(horizontal) and <y-offset> (vertical) values.
  • If a third value is given, it is interpreted as a <blur-radius>.
  • If a fourth value is given, it is interpreted as a <spread-radius>.
/* Keyword values */
box-shadow: none;

/* A color and two offset values */
/* <color> | <x-offset> | <y-offset> */
box-shadow: red 60px -16px;

/* Three offset values and a color */
/* <x-offset> | <y-offset> | <blur-radius> | <color> */
box-shadow: 10px 5px 5px black;

/* Four offset values and a color */
/* <x-offset> | <y-offset> | <blur-radius> | <spread-radius> | <color> */
box-shadow: 2px 2px 2px 1px rgb(0 0 0 / 20%);

1. x-offset

The horizontal offset of the shadow. It determines how far the shadow will be shifted horizontally.

  • Positive values move the shadow to the right.
  • Negative values move the shadow to the left.

2. y-offset

The vertical offset of the shadow. It determines how far the shadow will be shifted vertically.

  • Positive values move the shadow down.
  • Negative values move the shadow up.

3. blur-radius

The blur radius of the shadow. It controls the amount of blur applied to the shadow, making it softer or sharper.

  • Positive values create a blur effect. The larger the value, the more blurred the shadow becomes.
  • If omitted or set to 0, the shadow will be sharp with no blur.

4. spread-radius

The spread radius of the shadow. It controls the size of the shadow by expanding or contracting it.

  • Positive values cause the shadow to grow larger.
  • Negative values cause the shadow to shrink.
  • If omitted or set to 0, the shadow will be the same size as the element, with the blur-radius affecting only the softness.

In this example:

  • x-offset: 10px (The shadow is moved 10 pixels to the right)
  • y-offset: 5px (The shadow is moved 5 pixels down)
  • blur-radius: 15px (The shadow is blurred with a radius of 15 pixels)
  • spread-radius: 3px (The shadow is expanded by 3 pixels)
  • color: rgba(0, 0, 0, 0.5) (The shadow color is a semi-transparent black)
box-shadow: 10px 5px 15px 3px rgba(0, 0, 0, 0.5);

6. overflow

This property is a shorthand for- overflow-x and - overflow-y. This property controls what happens to content that is too large to fit into an element's box. It specifies whether to clip content, render scroll bars, or display overflow content. The possible values are:

  • visible: Content is not clipped and will render outside the element's box (default value).
  • hidden: Content is clipped, and no scroll bars are provided. Overflowing content will not be visible.
  • scroll: Content is clipped, but scroll bars are provided to see the rest of the content.
  • auto: Content is clipped, and scroll bars are added only if necessary to see the overflowing content.
  • inherit: The element inherits the overflow property from its parent.

7. opacity

This property specifies the transparency level of an element. It allows you to control how transparent or opaque an element and its content will be. The possible values are:

  • 0: The element is fully transparent, making it completely invisible.
  • 1: The element is fully opaque, meaning it is not transparent at all (default value).
  • 0.1 to 0.9: These values set the element to be partially transparent, with 0.1 being almost fully transparent and 0.9 being almost fully opaque. For example, 0.5 would make the element 50% transparent.

III. Positioning

Resources

1. Static vs. Relative Positioning

  • Static - position: static - is the default positioning value for all elements. Statically positioned elements are rendered in the document's normal flow, following the order in the HTML markup. They are NOT affected by top, right, bottom, or left properties.
  • Relative - position: relative - is similar to static, it follows the document’s normal flow. However, a relative element can be shifted from its normal position based on the top, right, bottom, and left values. The space originally occupied by the element is still reserved, and other elements will not move to fill that space.

2. Absolute Positioning

An absolute positioned element - position: absolute - is taken out of the normal document flow, and no space is created for the element in the page layout.

  • Its position is determined by its nearest positioned ancestor (an ancestor with a position value of relative, absolute, fixed, or sticky). If there is no such positioned ancestor, it is positioned relative to the initial containing block (typically the document <body> or the root element).
  • The top, right, bottom, and left properties specify the distance from the respective edges of the containing element.

A couple of good use cases for absolute positioning are:

  • modals
  • image with a caption on it
  • icons on top of other elements

Absolute positioning has very specific use cases and if possible, using flexbox or grid should be prioritized. Absolute positioning shouldn’t be used to do entire page layouts.

3. Fixed Positioning

A fixed positioned element is removed from the normal document flow, and no space is created for the element in the page layout.

  • Its position is relative to the viewport. It will remain in the same place, even if the page is scrolled.
  • The top, right, bottom, and left properties specify the distance from the respective edges of the viewport.
  • This position is especially useful for navigation bars, floating chat buttons, etc.

4. Sticky Positioning

Sticky elements are positioned according to the normal flow of the document, and will act like normal elements until you scroll past them, then they start behaving like fixed elements. It’s useful for things like section headings, nav-bars.

IV. CSS Functions

Resources

CSS Functions are reusable constructs that perform specific tasks based on the arguments they receive. They are often used to calculate values, manipulate colors, perform transformations, or generate gradients.

color: rgb(0, 42, 255);
background: linear-gradient(90deg, blue, red);

CSS doesn’t allow creating our own functions, the language comes with a list of premade functions that will help you solve the most common styling problems.

1. linear-gradient()

The linear-gradient() function creates an image that consists of a progressive transition between two or more colors along a linear path.

/* syntax */
background: linear-gradient(direction, color-stop1, color-stop2, ...);
  • direction: Specifies the direction of the gradient. It can be a degree (e.g., 45deg), a keyword (e.g., to right, to bottom left), or a default direction (to bottom if not specified).
  • color-stop: Represents a color in the gradient and optionally, where the color stop is positioned.
/* A gradient going from the bottom right to the top left corner,
starting blue and finishing red */
linear-gradient(to left top, blue, red)

/* Color stop: A gradient going from the bottom to top,
starting blue, turning green at 40% of its length,
and finishing red */
linear-gradient(0deg, blue, green 40%, red)

2. calc()

The calc() is used to perform calculations to determine CSS property values dynamically. This function allows you to mix different units (e.g., percentages, pixels, ems) and perform arithmetic operations directly within your CSS.

/* syntax */
property: calc(expression);
width: calc(100% - 50px);
font-size: calc(1em + 2vw);

Ensure there is whitespace around the operators. For example, calc(100% - 50px) is valid, but calc(100%-50px) is not.

3. min()

The min() allows you to select the smallest value from a list of comma-separated expressions.

/* syntax */
property: min(expression1, expression2, ...);
  • You can use min() to ensure an element has a minimum width, regardless of its content or other styles.
min-width: min(300px, 50%);

→ the minimum width of the element is set to 300 pixels or 50% of its container's width, whichever is larger.

  • Used to create responsive font sizes that adapt to different screen sizes.
font-size: min(1.2em, 5vw);

→ This sets the font size to the smaller value between 1.2 ems and 5% of the viewport width, ensuring readability while maintaining scalability.

4. max()

max() works the same way as min(), only in reverse. It will select the largest possible value from within the parentheses. You can think of max() as ensuring a minimum allowed value for a property.

width: max(100px, 4em, 50%);

The max function is most useful when the viewing window is either exceptionally small or the user increases the content size using the browser’s zoom feature.

5. clamp()

The clamp() function clamps a middle value within a range of values between a defined minimum and maximum bound. The function takes three parameters: a minimum value, a preferred value, and a maximum allowed value.

h1 {
font-size: clamp(320px, 80vw, 60rem);
}
  • the smallest value (320px)
  • the ideal value (80vw)
  • the largest value (60rem)

V. Custom Properties

Resources

Custom properties, also known as CSS variables, allow you to define and reuse values throughout your CSS. Custom properties can hold any valid CSS value, such as colors, font sizes, spacing values, or even entire property-value pairs.

  • We define custom properties using the -- syntax within the:root selector
/* Define custom properties */
:root {
--primary-color: #007bff;
--secondary-color: #6c757d;
--font-size-base: 16px;
--spacing-unit: 1rem;
}
  • These custom properties are then used throughout the CSS by referencing them with the var() function.
/* Use custom properties */
body {
color: var(--primary-color);
font-size: var(--font-size-base);
}

h1 {
color: var(--secondary-color);
margin-bottom: var(--spacing-unit);
}

1. var() function - fallback value

The var() function can accept two parameters. The first parameter is the custom property we want to assign, the second parameter is an optional fallback value. When a fallback value is provided in addition to the custom property, the fallback value will be used if the custom property is invalid.

var(--custom-property-name, fallback-value)
:root {
--primary-color: #007bff; /* Custom property defined */
--secondary-color: #6c757d;
}

body {
/* Uses the value of --primary-color, which is #007bff */
color: var(--primary-color, #000);
/* Uses the fallback value #f8f9fa because --tertiary-color is not defined */
background-color: var(--tertiary-color, #f8f9fa);
}

2. Scope

1. Global Scope

Custom properties defined within the:root selector have global scope, meaning they can be accessed and used throughout the entire document.

:root {
--global-color: #ff0000; /* Global custom property */
}

2. Component/Local Scope

Custom properties can also be defined within a specific selector, such as a class or an ID selector. In this case, the custom property has a component or local scope, meaning it is only accessible within the rules of that specific selector and its descendants.

<div class='cool-div'>
<!-- This paragraph is nested inside cool-div -> within cool-div scope -->
<p class='cool-paragraph'>Check out my cool, red background!</p>
</div>

<!-- This paragraph is not nested inside cool-div -> not within scope -->
<p class='boring-paragraph'>I'm not in scope so I'm not red or cool.</p>
.cool-div {
--main-bg: red;
}

.cool-paragraph {
background-color: var(--main-bg);
}

.boring-paragraph {
background-color: var(--main-bg);
}

3. Creating themes with custom properties

Demonstration

:root.dark {
--border-btn: 1px solid rgb(220, 220, 220);
--color-base-bg: rgb(18, 18, 18);
--color-base-text: rgb(240, 240, 240);
--color-btn-bg: rgb(36, 36, 36);
}

:root.light {
--border-btn: 1px solid rgb(36, 36, 36);
--color-base-bg: rgb(240, 240, 240);
--color-base-text: rgb(18, 18, 18);
--color-btn-bg: rgb(220, 220, 220);
}

prefers-color-scheme

The prefers-color-scheme media query is a CSS feature that allows you to detect the user's preferred color scheme and apply styles accordingly. It checks the user's operating system or browser settings and applies the corresponding styles for a light or dark theme.

/* Default theme (light) */
:root {
--bg-color: #fff;
--text-color: #333;
}

/* Dark theme */
@media (prefers-color-scheme: dark) {
:root {
--bg-color: #333;
--text-color: #fff;
}
}

body {
background-color: var(--bg-color);
color: var(--text-color);
padding: 20px;
}
  1. Only dark and light are valid values for the media query, so you can’t use prefers-color-scheme to implement any themes beyond these two basic ones.
  2. The light value for the media query is for when a user has a light theme specified or when they have no preference set.
  3. It doesn’t allow users to change the theme themselves, which can still be important in cases where a user might want to use the theme opposite of their OS/user agent's preferred one for whatever reason.

VI. Frameworks and Preprocessors

1. Frameworks

Resource

A CSS framework is a pre-written bundle of CSS code that provides pre-styled components and design patterns to help developers build websites and web applications more efficiently. Some popular examples are Bootstrap, Tailwind, Bulma, and Foundation.

The main idea behind CSS frameworks is to abstract away the process of writing CSS code for common UI elements, layouts, and responsive designs. They provide pre-defined classes that you can apply to your HTML elements to get a specific styling or behavior without having to write the corresponding CSS styles from scratch.

  • For example, Bootstrap provides a class called .btn that applies all the necessary styles to create a button with a consistent look and feel across the website. This can save developers time and effort compared to writing all the button styles themselves.

2. Preprocessors

Resource

A CSS preprocessor is a scripting language that extends the functionality of regular CSS by introducing features like variables, mixins, nesting, functions, and more. Some popular examples are Sass, Less, and Stylus.

The primary purpose of CSS preprocessors is to make writing and maintaining CSS code more efficient and organized. They provide tools and constructs that help developers write more modular, reusable, and scalable CSS code.

  • For instance, preprocessors allow developers to define variables to store commonly used values (like colors or font sizes), which can then be referenced throughout the CSS codebase. This makes it easier to update a value globally, without having to change it in multiple places.
  • Preprocessors also support features like nesting, which allows developers to nest selectors within one another, mimicking the hierarchical structure of HTML. This can make the CSS more readable and easier to understand.