Lecture 6 Foundational Concepts of CSS

CSS beyond the browser?

Web technologies can also be used to make apps that can be installed on a phone! [Progressive Web Apps](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps) is a set of technologies enabling this, and at the bare minimum, it requires a [manifest file](https://developer.mozilla.org/en-US/docs/Web/Manifest) (just a JSON file with metadata about the app) and a `<link rel="manifest" href="manifest.json" />` in the page’s `<head>`.

Size of CSS: ~1.6K “words”

Topics for today

In CSS, every element is a box

A lot of CSS is about how these boxes are painted, and how they affect other boxes

Lorem Ipsum dolor sit amet
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor
Lorem Ipsum
ad minim veniam
ullamco laboris
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
velit esse cillum dolore
consectetur adipiscing elit, sed do eiusmod tempor incididunt
Excepteur sint occaecat cupidatat non proident

CSS properties

h1 { font-size: 200%; color: white; }

Document flow & the box model

Block elements display: block;

  • Laid out top to bottom*
  • Line break before and after
  • Box as wide as available space (extrinsic)
  • Box as tall as all contents (wrapped)
  • Text wrapping inside box
Lorem Ipsum dolor sit amet
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Inline elements display: inline;

  • Laid out left to right*
  • Inline with surrounding text
  • Box as wide as all contents (intrinsic)
  • Box as tall as one line
  • Text wrapping by box fragmentation
velit esse cillum dolore
consectetur adipiscing elit
sed do eiusmod tempor incididunt
Excepteur sint occaecat cupidatat non proident
\* In Western languages. Which elements that we have seen already are block and which inline?

Which of the elements we have seen so far are block and which inline?

div span p em ol ul strong article abbr details

Block Inline

Note that many of the elements we have seen are missing from this list. That’s because block and inline are not the whole story.
The display property How does the browser know which elements are block and which are inline? It’s not magic, that is also just CSS that is being applied to elements by default, called the *User Agent Stylesheet*. Open this in a new tab and inspect the paragraphs, `<em>` and `<strong>` elements, or even the `<body>`, `<head>` and `<html>` elements. We can set the `display` property ourselves as well, to override the defaults. Let's make the paragraphs inline and the `<em>` and `<strong>` elements block!

Each property has an initial value

display: initial; → display: inline;
font-weight: initial; → font-weight: normal;
- We can set a property to its initial value by setting it to the keyword [`initial`](https://developer.mozilla.org/en-US/docs/Web/CSS/initial), which is valid for every property (it is a *global keyword*) - How do we find out what a property’s initial value actually is? By looking up its definition. E.g. here’s [`display`](https://developer.mozilla.org/en-US/docs/Web/CSS/display#formal_definition) - Usually the initial value is the value that produces no effect, e.g. [`font-weight: normal`](https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight#formal_definition)

Browsers apply default CSS through their User Agent Stylesheet


			html {
				display: block;
			}

			body {
				display: block;
				margin: 8px;
			}

			article,
			aside,
			details,
			div,
			dt,
			figcaption,
			footer,
			form,
			header,
			hgroup,
			html,
			main,
			nav,
			section,
			summary {
				display: block;
			}

			head, link, meta,
			script, style, title {
				display: none;
			}
		
That is just a CSS file that is applied to every website. Curious what that looks like? Here are the UA stylesheets of some popular browsers: - [Chrome](https://source.chromium.org/chromium/chromium/src/+/master:third_party/blink/renderer/core/html/resources/html.css;bpv=0) - [Firefox](https://github.com/mozilla/gecko-dev/blob/master/layout/style/res/html.css) - [Safari](https://trac.webkit.org/browser/trunk/Source/WebCore/css/html.css)

The combination of initial values and UA stylesheet values are the defaults we start with

Any CSS we write has priority over these defaults. We will discuss the exact mechanism in the next lecture.
The Box Model

There are a few properties that are crucial to how these boxes are sized:

  • padding controls the spacing from the element’s content to its edge. margin specifies the spacing from the element’s edge to the elements around it, and can be negative to bring the element closer to other elements instead of farther. border allows you to specify a visible border. It’s placed outside the padding.
  • Padding & border widths are added to width and height, not subtracted from it.
  • Outlines, shadows etc do not affect the box in any way.
  • Always use sufficient padding, otherwise the text is uncomfortable to read. As you may remember from the Graphic Design lecture, you usually need more padding horizontally than vertically.

Switching box model


				textarea {
					padding: 1em;
					width: 100%;
				}
				­
			

				textarea {
					padding: 1em;
					width: 100%;
					box-sizing: border-box;
				}
			
Switching box models Sometimes the default box model is a problem. Let's look at an example. We want this textarea to have a width of 100%. However, if we set `width: 100%` it doesn’t work the way we expect. What is happening? How can we fix it? We *can* fix it with `calc(100% - 1em)` but that way we are repeating the padding and border twice. How can we improve our code? Let’s apply `box-sizing` to make our code more maintainable.
Alternate Box Model
  • What happens when we specify a width of 100%? Does it match our intent?
  • We can change that with box-sizing: border-box, but that has its issues too
  • Each element has a content box, a padding box, a border box and a margin box that are delimited by the corresponding areas.
  • You can use the browser developer tools to inspect the box model, via the "Computed" tab in the Elements panel.
Box properties on inline element The properties we saw behave entirely differently on inline elements! - `width` and `height` have no effect - `padding` and borders are applied to each fragment individually, and does not move it in any way. - Horizontal `margin` applies spacing before and after the element, vertical margin does nothing.

Block or inline?

<input>: Block or inline?

WUT?! 🤔🤔🤯

Block elements

  • Laid out top to bottom*
  • Line break before and after
  • Box as wide as all available space
  • Box as tall as all contents (wrapped)
  • Text wrapping inside box
  • We can set width, height, margin
Lorem Ipsum dolor sit amet
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Inline elements

  • Laid out left to right*
  • Inline with surrounding text
  • Box as wide as all contents
  • Box as tall as one line
  • Text wrapping by box fragmentation
  • width, height, margin have no effect.
velit esse cillum dolore
consectetur adipiscing elit
sed do eiusmod tempor incididunt
Excepteur sint occaecat cupidatat non proident

Inline-block elements

  • Laid out top to bottom*
  • Line break before and after
  • Box as wide as all available space
  • Box as tall as all contents (wrapped)
  • Text wrapping inside box
  • We can set width, height, margin-top, margin-bottom
  • Laid out left to right*
  • Inline with surrounding text
  • Box as wide as all contents
  • Box as tall as one line
  • Text wrapping by box fragmentation
  • width, height, margin-top, margin-bottom have no effect.
velit esse cillum dolore
Lorem Ipsum dolor sit amet
consectetur adipiscing elit
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
sed do eiusmod tempor incididunt
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Excepteur sint occaecat cupidatat non proident

Block, inline, or inline block?

  • Is image a block or inline element? Or maybe inline-block?
  • We can set width, height, margin, although they are not entirely independent
  • If we inspect, it's inline (?!?) What's going on?
  • Tip: object-fit and object-position help us adjust how the object contained within the replaced element should be positioned.

Replaced elements

Examples

Block elements

div h1 h2 h3 h4 h5 h6 p body html

Inline elements

a span strong em mark code cite abbr

Inline-block elements

button input textarea select

Replaced elements

img video audio iframe canvas option object embed
  • Even elements like head or style are boxes too!
  • They just have display: none from the User Agent stylesheet, which hides the element and removes it from normal flow.
  • Same with elements with the hidden attribute
  • This is a design principle of the web platform: as much as possible, its behavior must be explainable through its own primitives, not special "magic"
  • You can also apply display: none to elements you want to hide.

Other useful layout modes

Flexbox One dimension
Grid Two dimensions
Block, inline, and inline block were the first layout modes, and were designed to support **document layout**. Though block and inline do play a huge role in today's layouts, these days we also have other layout modes that support creating UIs much better. We will explore them in more detail in the Layout lecture.

Selectors

h1 { font-size: 300%; line-height: 1; }
  • The selector tells the browser which elements the rule is about, in this case h1 headings.
* { margin: 0; padding: 0; box-sizing: border-box; }
  • The universal selector allows us to apply certain declarations to every element in the page.
  • It can be useful for undoing certain user agent styles, but also really dangerous. E.g. * {font-weight: normal} might seem like a reasonable default, but it means our strong elements also will start from the same non-bold baseline.

Values & Units

width: 400px;

width: 50%;

width: 400px;

width: 400px;

<length>

Read more about lengths

<length> units

Absolute

Same measurement everywhere

Examples: px, cm, mm, in, pt

Relative

Dependent on another factor

  • Font-relative units scale with the text size
    Examples: em, ch, rem, ex
  • Viewport-relative units scale with the viewport
    Examples: vh, vw, vmin, vmax
  • Some useful units are:
    • em is relative the current font size
    • rem is relative to the current font size of the html element
    • ch represents the width of the "0" glyph in the current font. This is useful in monospaced fonts where every character has the same width.
    • vw is a percentage of the viewport width (100vw = viewport width)
    • vh is a percentage of the viewport height (100vh = viewport height)
  • Font-relative units are useful in making components that scale by changing one property (the font-size).
  • Viewport-relative units are useful in making layouts that scale nicely with the viewport size.

Font relative units


				button {
					background: yellowgreen;
					border: 1px solid olivedrab;
					padding: 15px;
					border-radius: 6px;
					font-size: px;
					line-height: 32px;
				}
			

				button {
					background: yellowgreen;
					border: 1px solid olivedrab;
					padding: .5em;
					border-radius: .2em;
					font-size: px;
					line-height: 1em;
				}
			
Why are font-relative units useful? Let’s look at an example. Put your caret on the font-size and press Shift + to increase the font size fast. Note that the rest of the styling (padding, line-height, border-radius etc) becomes disproportionately small. If we use font-relative units, we can make everything adapt! Our button is now nicely scalable by changing only one parameter. We can even set font-size itself to font-relative units, if we want it to adapt to the surrounding font size!

Viewport relative units


				h1 {
					font-weight: 300;
					font-size: 4em;
				}
			

				h1 {
					font-weight: 300;
					font-size: 12vw;
				}
			
  • Viewport scalable units allow us to scale text to the viewport size.
  • calc allows us to perform calculations with different units. The type returned depends on the units that participate in the calculation.
  • min, clamp and max allow us to create upper and lower bounds for our values, and can participate in calc() expressions too.
  • All these functions can of course be combined, e.g. min can be used inside calc and vice versa.
  • Note that unlike Python or most other programming languages, these calculations are re-evaluated every time anything changes (e.g. the viewport or font size)

width: 50%;

Percentages

Each property defines whether percentages are allowed and what they represent

margin-left: 10%;

margin-top: 10%;

border: .3em steelblue;

outline: auto;

margin: auto;

overflow: auto;

Lorem Ipsum dolor sit amet

CSS-wide keywords

Other datatypes

opacity: [num];

transform: rotate([angle]deg);

background-color: hsl([hue], 100%, 40%);

background: url(img/chocolate-mousse.png) center / cover, linear-gradient(teal [stop]%, gold);

background: conic-gradient(teal [stop]%, gold);

Percentages do not always resolve to lengths. E.g. in conic gradients, they resolve to angles, since color stops are placed along a *gradient circle*.

Selectors

h1 { font-size: 300%; line-height: 1; }
  • The selector tells the browser which elements the rule is about, in this case h1 headings.
* { margin: 0; padding: 0; box-sizing: border-box; }
  • The universal selector allows us to apply certain declarations to every element in the page.
  • It can be useful for undoing certain user agent styles, but also really dangerous. E.g. * {font-weight: normal} might seem like a reasonable default, but it means our strong elements also will start from the same non-bold baseline.

Simple Selectors

Most Common simple selectors * element .class #id
Attribute selectors [attr] [attr="value"] [attr^="starts"] [attr$="ends"] [attr*="anywhere"] [attr~="anywhere"]
Pseudo-classes (sample) :hover :focus :checked :target :nth-child() :invalid
  • These are called simple selectors because they represent a single selection criterion.
  • Here, they are listed by frequency of use: the selectors that are less frequently used are more transparent.
  • More frequency of usage data for selectors

Compound selectors

input[type="text"]
=
input AND [type="text"]

Later in this lecture we will learn more ways to apply logical operators to CSS selector queries.

Concatenation = AND

img [loading=lazy] img[loading=lazy]
<img src="cat.jpg" alt="">
<img src="cat.jpg" alt="" loading="lazy">
<iframe src="widget.html" loading="lazy">

Concatenating simple selectors intersects them

Concatenating simple selectors intersects them (i.e. it is equivalent to an AND operation). The result is called a Compound Selector E.g. a.docs matches an <a> element with the class docs. Note that element selectors can only be first in the sequence (e.g. [title]element is invalid)

Pseudo-classes

button:hover { background: greenyellow; }
  • Pseudo-classes allow us to style states of an element, like an imaginary class that is added and removed automatically.
  • Does it remind you of JS? Indeed, a lot of pseudo-classes are inspired from functionality that used to require JS. However, using pseudo-classes is a lot easier than writing JS because the browser keeps track of when to apply and unapply the state for you.

Pseudo-classes are simple selectors that allow us to style states of an element.

- Here we have a button that does not react at all when we interact with it. Furthrermore, it is not clear that the second one is disabled. - Which dimensions of usability suffer? - Let’s use pseudo-classes to improve the experience. - Here we can see some example pseudo-classes in action: - `:enabled` and `:disabled` (what's the advantage over just using a `[disabled]` attribute selector?) - `:hover` matches when the pointer is over the element - `:active` matches when the pointer is pressed on the element - `:hover` and `:active` behave differently on mobile - `:focus` matches when the element is focused, iff it can receive focus - Note that by chaining simple selectors together we are intersecting them. We will learn more about this later today.
Here is a different style for this list. We want to eliminate the line on the last item. What can we do?

Pseudo-classes can be dynamic (activity-based) or structural (DOM tree-based)

There are also logical pseudo-classes, and we will cover them later today.

Pseudo-classes

Dynamic

  • Mouse over element :hover
  • Mouse down on element :active
  • Currently focused :focus
  • Contains currently focused :focus-within
  • Checked radio or checkbox :checked
  • Elements targeted by the url #hash :target

Structural

  • first child :first-child
  • last child :last-child
  • only child :only-child
  • odd children :nth-child(odd)
  • every 4th paragraph after the 8th from the end p:nth-last-of-type(4n + 8)
  • Structural pseudo-classes match elements based on their position among their siblings in the tree. Here we explore the :*-child family of pseudo-classes.
  • :first-child and :last-child match elements who are first and last among their siblings, respectively.
  • :only-child is equivalent to :first-child:last-child
  • :nth-child() is a generalization. It accepts an argument of the form An+B (e.g. 2n+1) and matches elements whose position among their siblings matches the argument for some non-negative integer n. A and B can be negative.
    • :nth-child(even) is equivalent to :nth-child(2n) and matches each 2nd, 4th, 6th etc child.
    • :nth-child(odd) is equivalent to :nth-child(2n+1) and matches each 1st, 3rd, 5th etc child.
    • :nth-child(3n+1) matches every 3rd child starting from the first.
    • :nth-child(1) is equivalent to :first-child
    • Activity: Select the first 4 items. Now select items 2-5!
  • :nth-last-child() is exactly the same as :nth-child() but starts counting from the end.
    • :nth-last-child(1) is equivalent to :last-child
    • Activity: Select the first item, only when the total count of its siblings is ≥3
  • There is also a :*-of-type family of pseudo-classes, with exactly the same syntax, that only counts elements of the same type. You can experiment with it here

Combinators

We have now added a nested ordered list, and our rule seems to be applying to that too. What can we do?

Combinators allow us to match on relationships between elements

:checked + label

Next sibling combinator

label directly after a or

label that contains a or

label directly after or

h1 ~ h2

Subsequent Sibling combinator


					<h1>…</h1>
					<h2>…</h2>
				

					<h1>…</h1>
					<p>
					<h2>…</h2>
				

					<h1>…</h1>
					<section>
						<h2>…</h2>
					</section>
				

					<h2>…</h2>
					<h1>…</h1>
				

Combinators cannot look backwards
but :has() can!

  • Images inside paragraphs: p img
  • <h2>s right after <h1>s: h1 + h2
  • Paragraphs that contain images: p:has(img)
  • <h1>s that come before <h2>s: h1:has(+ h2)
- [`:has()`](https://developer.mozilla.org/en-US/docs/Web/CSS/:has) is fairly new, and until recently there was *no way* to do the kinds of things in the last two examples

Compound selectors + combinators
=
complex selectors

Terminology recap

Term Meaning Example
Simple selector Single criteria .foo
Compound selector Intersection of simple selectors .foo.bar
Complex selector Multiple Compound selectors ul.bar li
Selector list Multiple comma separated complex selectors ul.bar li, .foo

Logical operations

CSS selectors express sets of HTML elements

How to perform set operations? (¬, ∪, ∩)

Logical operations we've seen

  • AND: p.warning = p.warning
  • OR: td, th = tdth
  • NOT via overrides:
    a { text-decoration: none }
    a:hover { text-decoration: underline }
We've already seen some ways to perform logical operations. And for many years in CSS' history, these were the only ways. But they are quite limited. How do we express AND or OR between complex selectors? And how do we negate without having to explicitly set both states?

Negation pseudo-class: :not()

With overrides


					a {
						text-decoration: none;
					}

					a:hover {
						text-decoration: underline;
					}
				

With negation


					a:not(:hover) {
						text-decoration: none;
					}
				
The negation pseudo-class allows us to express our intent more clearly. It also means we can negate without having to set both states.

AND/OR between complex selectors:
The :is() pseudo-class


				:is(h1, h2, h3, h4, h5, h6):is(section *, article *) {
					border-bottom: .1em solid slategray;
				}
			
Note that this particular selector can be rewritten more succicntly as `:is(section, article) :is(h1, h2, h3, h4, h5, h6)`

Pseudo-elements

Pseudo-elements allow us to style parts of an element

- Similar to how certain pseudo-classes represent additional state information not directly present in the document tree, a pseudo-element represents an element not directly present in the document tree. They are used to create abstractions about the document tree beyond those provided by the markup. - [Specification on pseudo-elements](https://www.w3.org/TR/selectors/#pseudo-elements)

Examples

List markers


					li::marker {
						color: red;
					}
					­
				

Input placeholders


					input::placeholder {
						color: slategray;
					}
					­
				

Text selection


					::selection {
						background: rebeccapurple;
						color: white;
					}
				
Note that some pseudo-elements can be very limited in what properties they support. E.g. try applying `filter` on `::marker` or `::selection`.

Pseudo-elements also allow us to insert content

Topics for next week