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
Document flow
The box model
Values and units
Selectors
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
E.g. `font`, `text-align`, `color`, `word-spacing`, …
Some style the boxes that contain the text
E.g. `width`, `height`, `padding`, `border`, `background`, …
Some also affect other boxes around the element
E.g. `margin`, `display`, `position`, …
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?
divspanpemolulstrongarticleabbrdetails
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!
- 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
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 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?
It was on the same line → inline.
It had a width → block
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.
Theory: An element whose representation is outside the scope of CSS
Practice: flows around surrounding content like an inline-block, contents are not affected by the current document's styles
Only replaced elements can have intrinsic dimensions or an intrinsic aspect ratio (but not all do).
Examples
Block elements
divh1h2h3h4h5h6pbodyhtml
Inline elements
aspanstrongemmarkcodeciteabbr
Inline-block elements
buttoninputtextareaselect
Replaced elements
imgvideoaudioiframecanvasoptionobjectembed
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.
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>
A measure of distance
Some properties allow negative lengths, others do not
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
inherit
initial
unset
revert
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.
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
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.
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)
html
head
meta
title
link
body
header
h1
ul
li
li
li
li
li
li
img
li
li
li
li
li
strong
li
li
li
li
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
- [`: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
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?
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)
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 can also generate new content (aptly called generated content)
::before emulates a text node inserted in the beginning of an element and ::after a text node inserted after every other child.
While they were originally created for inserting actual text, these days they are often used to insert extra empty boxes (content: "")
that are then styled with CSS to create various decorations
You cannot nest pseudo-elements to generate multiple bits of content, i.e. ::before::before is invalid (though there are discussions about enabling it in the future).