Lab 2
Styling with CSS

DSC 209: Data Visualization • Slides by Lea Verou

		h1 {
			color: deeppink;
		}
	
CSS is largely a collection of *rules*. A CSS rule consists of a *selector* and one or more *declarations*. - The _selector_ specifies which elements the rule applies to. The simplest selector is the one shown here that applies to all elements of a certain type, in this case `h1` elements. However, CSS selectors go a lot deeper than that, and can query elements based on attributes, relationships to other elements, user activity, and more. - _Declarations_ are key-value pairs, where the key is the _property_ and the value is the _value_ of that property. Each declaration sets one aspect of the style of the element. - Yes, it is possible for declarations from multiple rules (or even the same rule) to conflict. There is an entire conflict resolution algorithm to determine which one wins in these cases, called The Cascade, which is where CSS gets its name from (Cascading Style Sheets). We will cover this briefly in the next lab.

Adding CSS

Linking


			<link rel="stylesheet" href="style.css" />
		

CSS @import


			@import url("colors.css");
		

			<style>
				@import url("style.css");
			</style>
		

Embedding


			<style>
				h1 {
					color: deeppink;
				}
			</style>
		

Inline styles avoid


				<h1 style="color: deeppink;">
					Hi there
				</h1>
			
There are several ways to add CSS to an element or a page, each with its own tradeoffs. - Generally, linking to a separate CSS file is preferable because that facilitates reuse across several pages, and allows the browser to cache the CSS file for faster loading on subsequent visits. - Typically, `<link>` is used for that, and then the CSS file can use `@import` to pull together several CSS files. - For prototyping, you can also embed CSS on the page (inside a `<style>` element), or even inline it directly on the elements themselves so you don’t have to figure out a selector. The latter is best avoided for general styling, but it can be useful for e.g. passing data via [CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties), debugging, and other one-off uses.

Layout

Every HTML element produces a box

Toggle boxes
- Every HTML element generates a box. This is the foundation of CSS layout. - Yes, even "special" elements like `<body` or `<html` generate boxes and can be styled like any other box. Yes, even `<head>`, it’s just that its also hidden. - A lot of CSS is about how these boxes are painted, and how they affect other boxes - Explore what boxes the websits you visit are made of by dragging this "Toggle boxes" link to your bookmarks bar and clicking it on any website.
There are many CSS properties that affect the dimensions of an element. The way this happens is called the _box model_. - `margin` and `padding` affect spacing on the outside and inside of the box, respectively. - `border` affects the box dimensions, whereas `outline` is painted on the outside and does not affect the box’s geometry - By default `width` and `height` specify the dimensions of the content area, and padding and border are added to that. You can change this with the `box-sizing` property.

Units


			<h1 style="font-size: 32px; padding: 13px; border: 3px solid black">Make me bigger</h1>
			<h1 style="font-size: 200%; padding: 0.4em; border: 0.1em solid black">Make me bigger</h1>
			<h1 style="font-size: 2.5vw; padding: 1vw; border: 0.2vw solid black">Make me bigger</h1>
		
There are many units in CSS, and we will not cover them all. The main distinction to keep in mind is relative vs absolute: - Relative units are relative to something else, such as the font size of an element, or the viewport size. - Absolute units are fixed, such as inches, centimeters, or pixels. - Percentages are also relative, but what they are relative to depends on the property they are used on (and is explained in the property’s documentation). Picking a suitable unit can make your CSS code more maintainable and adaptable to different conditions and future changes.
Block elements display: block; Inline elements display: inline;
Laid out Vertically Horizontally
Box width Available space Contents
Box height Contents One line
Text wrapping Inside box Fragmentation
Example elements <h1> <p> <ol> <ul> <section>
<div> (Generic)
<strong> <em> <a>
<span> (Generic)
Lorem Ipsum dolor sit amet
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Excepteur sint occaecat cupidatat non proident, sunt in culpa.
Lorem, ipsum.
velit esse cillum dolore
consectetur adipiscing elit
Ipsa, velit.
sed do eiusmod tempor incididunt
Excepteur sint occaecat cupidatat non proident
- There are two main types of elements: block and inline, with different layout characteristics. - There is also `inline-block`, a hybrid of the two. - While certain elements are block or inline by default, you can change this with the `display` property.

Layout modes for UIs

Flexbox One dimension
Grid Two dimensions
- Block, inline, (and inline-block, a hybrid of the two) were the first layout modes, designed primarily for documents. While they play a huge role in today's layouts, these days we also have other layout modes that support UI design much better, namely [Flexbox](https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Flexbox) and [Grid Layout](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout/Basic_concepts_of_grid_layout). - The core idea around Flexbox is that we apply `display: flex` to an element, and then we can control the layout of its children along a single axis (row or column), with easy controls for alignment, distribution, and order. - Grid Layout is a two-dimensional layout system, which means it can handle both rows and columns at the same time. We enable grid on an element with `display: grid`, and define our grid with the `grid-template-*` properties (usually `grid-template-columns`). Elements are then placed on grid cells sequentially, but we can override the automatic placement with `grid-column`, `grid-row`, and `grid-area`. - An element’s children can take part in the same grid, via [subgrid](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout/Subgrid). - Grid is more powerful, but also more complex, and it is often used in combination with Flexbox. Learn more: - [Flexbox Froggy: A game for learning Flexbox](https://flexboxfroggy.com/) - [Grid Garden: A game for learning Grid](https://cssgridgarden.com/)

Selectors

HTML pages are trees

What we wrote


				<!DOCTYPE html>
				<title>Hello world</title>
				<h1>Hello world!</h1>
				<p>This is my <em>first</em> web page! 🤯
			

What the browser generated


				<!DOCTYPE html>
				<html>
					<head>
						<title>Hello world</title>
					</head>
					<body>
						<h1>Hello world!</h1>
						<p>This is my <em>first</em> web page! 🤯</p>
					</body>
				</html>
			

What was displayed

DOM Tree

Document node
Element node
Text node
- The nesting of HTML elements forms a tree, called the DOM tree. - This is what you are exploring in the browser dev tools, when you expand and collapse an element. - Trees are a very common data structure in computer science, and they are used to represent hierarchical relationships. - We use concepts from family trees to describe the relationships between tree elements, so we talk about children, parents, siblings, ancestors, and descendants.

Styling by 🌳 relationship

Rule nesting


				h1 {
					line-height: 1.1;

					em {
						color: rebeccapurple;
					}
				}
			

Descendant selectors


				h1 {
					line-height: 1.1;
				}

				h1 em {
					color: rebeccapurple;
				}
			

				<h1>
					Yo <strong><em>hello</em></strong> <em>world</em>!
				</h1>
				<p>This is my <em>first</em> web page! 🤯
			
- You can take advantage of these relationships to scope our CSS more narrowly. - For example, instead of styling all `<em>` elements, you can style only those that are inside `<h1>` elements (at any level). This is done with a [descendant selector](https://developer.mozilla.org/en-US/docs/Web/CSS/Descendant_combinator), or by simply nesting rules inside each other. - While we are only covering descendants here, there are other types of relationships you can style based on, such as direct children ([`>`](https://developer.mozilla.org/en-US/docs/Web/CSS/Child_combinator)), siblings ([`~`](https://developer.mozilla.org/en-US/docs/Web/CSS/Subsequent-sibling_combinator), [`+`](https://developer.mozilla.org/en-US/docs/Web/CSS/Next-sibling_combinator)), and more.

Targeting by identifier

Example Selector(s)
Class

						<a class="current" href="/">Home</a>
					
.current [class~="current"]
Id

						<section id="publications">
							...
						</section>
					
#publications [id="publications"]
Name

						<input name="subject" />
					
[name="subject"]
While we *could* target any element by using its element type and position in the tree, there are cases where that would be fragile. We can also assign identifiers to elements, and then target them by those identifiers. - The most common such identifier is the `class` name, which is a many-to-many relationship: each element can have many classes (space-separated) and each class can be assigned to any number of elements, anywhere. - There are also ids, assigned via the `id` attribute. These are supposed to be unique within the document (though this is not enforced). Beyond CSS, you can also link to elements with an id by adding a `#id` at the end of the page URL and they create global variables in JS (as we saw in [this slide](#reactive)). - There are also names, specified via the `name` attribute, mostly used these days to specify the name of form fields that will be submitted. There is no dedicated CSS syntax to target these, but regular [attribute selectors](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors) work fine. - You can also use attribute selectors to target any attribute, not just these three and there are different types of attribute selectors, based on whether you’re testing for presence (simply `[attribute]`), equality (`[attribute="value"]`), etc.

Basic logical operations

Intersection (AND)

by concatenation

a.current =

Match elements that match both a AND .current

Union (OR)

via commas

h1, h2, h3
=

Match elements that match either h1 OR h2 OR h3

CSS selectors express sets of HTML elements. So how to perform set operations? (¬, ∪, ∩)? [Learn CSS Selectors by playing a game](https://flukeout.github.io/)

Pseudo-classes


				<a href="https://github.com/leaverou"
				   target="_blank">
					GitHub
				</a>
			

				a {
					text-decoration: none;

					&:hover { /* becomes a:hover */
						text-decoration: underline;
					}
				}
			

				a:not(:hover) {
					text-decoration: none;
				}
			
## Types of pseudo-classes - **Dynamic:** `:hover`, `:focus`, `:active`, etc. - **Structural:** `:has()`, `:nth-child()`, etc. - **Logical:** `:not()`, `:is()`, `:where()`
- We add clases to group elements that have something in common. [Pseudo-classes](https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes) are added and removed automatically by the browser based on the element’s state or context. - For example, `:hover` matches elements that the user is currently hovering over. - Note that while this is effectively a conditional, it works differently than conditionals in most programming languages, which are executed once. In CSS, all logic applies *reactively* and is re-evaluated whenever the state changes. Which brings us to the next slide...

CSS is reactive

- CSS is _reactive_, meaning that it responds to changes automatically. We never have to think about when to update or re-render things. - Here we are using a little JS to change the value of a `--hue` CSS variable when we move the slider, which is used in the CSS to set the hue of several colors. - Notice how all colors respond automatically as the variable changes ("reactively") - This is also showcasing several CSS features: - [Variables in CSS](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties) are [properties starting with `--`](https://developer.mozilla.org/en-US/docs/Web/CSS/--*). Their value can be used in other properties with the [`var()`](https://developer.mozilla.org/en-US/docs/Web/CSS/var) function. They are available not only on the element they are defined on, but also its _descendants_. - So far we had only seen [keywords](https://developer.mozilla.org/en-US/docs/Web/CSS/named-color) being used for colors. While these are nicely readable, they are not very flexible, and the names are often inconsistent (e.g. `darkgray` is lighter than `gray`!!). There are several other ways to define colors in CSS, and here we are using [`oklch()`](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/oklch) colors, which are defined by a lightness, a chroma (how bright the color is, gray is 0), and a [hue](https://en.wikipedia.org/wiki/Hue) (which defines where the pure color would be on a color wheel). If you're more comfortable with hex colors or RGB don't worry, you can use those too. - The code on this slide is editable, try playing with it!

Conflicts

All else being equal, the last declaration wins


				h1 {
					color: seagreen;
					color: indianred;
				}
			

				h1 {
					color: indianred;
				}
				h1 { color: seagreen; }
			

				h1 {
					color: darkgoldenrod;
					color: lightgoldenrod;
				}
			
What happens if there are multiple conflicting declarations for the same property, on the same element? - There is an entire algorithm about how these conflicts are resolved, called Cascade & Inheritance. The Cascade is where CSS gets its name: Cascading Style Sheets. - At the very bottom of this algorithm is source order: all else being equal, the last declaration wins. What do we mean by all else being equal? We’ll get to that in a bit. - One thing to remember is that CSS ignores things it doesn’t understand, so if the last declaration is invalid, the previous one will still apply. - There is a caveat to this: If you’re using CSS variables, because the browser doesn’t know whether your declaration is valid until it's too late.

Shorthands & Longhands


				body {
					font: 100%/1.5 system-ui;
				}
			

Quiz

Are these equivalent?


						h1 {
							font-weight: bold;
							font: 100% system-ui;
						}
					

						h1 {
							font: 100% system-ui;
							font-weight: bold;
						}
					

				body {
					font-style: initial;
					font-variant-caps: initial;
					font-variant-ligatures: initial;
					font-variant-numeric: initial;
					font-variant-east-asian: initial;
					font-variant-alternates: initial;
					font-size-adjust: initial;
					font-kerning: initial;
					font-optical-sizing: initial;
					font-feature-settings: initial;
					font-variation-settings: initial;
					font-variant-position: initial;
					font-weight: initial;
					font-stretch: initial;
					font-size: 100%;
					line-height: 1.5;
					font-family: system-ui;
				}
			
Remember this CSS rule from Lab 1? What does it actually mean? - This is an example of a shorthand property, which is a single property that sets multiple properties at once. - When we set the `font` property, it actually sets 17 (!) properties for us, whether we provide values for them or not. - Properties we don’t set values to are set to their initial value, which is different for every property. - The special keyword `initial` resolves to the initial value of the property at hand.

Origin & Inheritance

- Even if you don't apply any CSS to your page, the browser will. That is called the _user agent stylesheet_. That’s what makes headings bigger, links blue, the `<em>` element italic, etc. - CSS applied by the browser has lower precedence than CSS applied by the website. - Notice that some properties _inherit_ from their parent, while others don’t. We did not have to specify a `font-family` or `line-height` for every element on our page, applying to the `<body>` was enough. - Which properties inherit? It depends on the property, but generally text-related properties do (`font-family`, `line-height`, `color`, etc.), while layout or box-related properties don’t (e.g. `width`, `height`, backgrounds, borders). - Values applied directly to an element always have higher precedence than inherited values.
Each selector gets a score based on its structure, that gives it higher precedence. That score is called specificity. It is calculated by counting the different criteria in a selector. Learn more: - [MDN page specificity](https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity) - Specificity battle

Specificity in action


				<nav>
					<a href="/" class="current">Home</a>
					<a href="/contact/">Contact</a>
				</nav>
			

				a.current {  /* specificity = 0,1,1 */
					text-decoration: none;
				}
				a { /* specificity = 0,0,1 */
					text-decoration: underline;
				}
			

				<nav id="nav">
					<a href="/" class="current">Home</a>
					<a href="/contact/">Contact</a>
				</nav>
			

				#nav a { /* specificity = 1,0,1 */
					text-decoration: underline;
				}
				a.current {  /* specificity = 0,1,1 */
					text-decoration: none;
				}
			
- Specificity is essentially a heuristic that tries to infer importance from querying logic. - Sometimes it matches our expectations, other times it does not. - For the cases where the default specificity does not match our intent, there are ways to both *increase* specificity, as well as to *decrease* it (`:where()`).

Putting it all together

  1. Inheritance: Direct > Inherited
  2. Origin: Website > Browser
  3. Specificity: Higher > Lower
  4. Source order: Last (valid) declaration wins
While these are the most common factors that infleunce conflict resolution, and the ones you are most likely to run across, there are also others too, such as scoping proximity, `!important`, layers, transitions & animatons. You can read about [the full algorithm on MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/Cascade).