CSS exercises following the Learn CSS module. Code is available on my CodePen collection.
- CSS
A CSS selector maps to an HTML element and apply styles to it.
- Universal selector
* {
color: blue;
}
- Type selector
article {
color: blue;
}
- Class selector
.my-class {
color: blue;
}
- ID selector
#my-id {
color: blue;
}
- Attribute selector
[my-attr='certain-value'] {
color: orange;
}
We can add s
to a case-sensitive attribute selector or do the opposite-case insensitivity by adding i
.
[my-aTtr='cErTain-Value' s] {
color: orange;
}
Specifying an attribute selector under a specific HTML element.
p[my-attr='certain-value'] {
font-size: 1.2em;
}
Three options to match portions of values.
/* A href that contains "example.com" */
[href*='example.com'] {
color: red;
}
/* A href that starts with https */
[href^='https'] {
color: green;
}
/* A href that ends with .com */
[href$='.com'] {
color: blue;
}
Any element that has an id
attribute must have a unique value for it. The best practice is not to use ID selectors but instead, use class selectors to reuse CSS.
Pseudo-classes applies CSS based on state changes, which means that the design can react to user input such as an invalid email address [11]. Other than valid
and invalid
, an HTML element has a multitude of states. Check out MDN HTMLElement to see more.
Below shows some examples of interactive states.
State | Required interaction |
---|---|
:hover |
Move the mouse pointer to the element. |
:active |
Click and hold on the <button> or <a> element. |
:focus |
Click on a <button> , <a> , <input> or <select> while it is not focused. |
:focus-within |
Any of its descendants is focused. |
:focus-visible |
The element receives focus via the keyboard (the tab key). |
:target |
Select an element that has an id matching a URL fragment. For example, given a link<a href="#sec2>Section 2</a> , while the user click on the link, it jumps to the HTML element with id=sec2 . The CSS style id:target{} works when the URL becomes xxx.com#sec2 . |
State | Required interaction |
---|---|
:link |
Work on <a> . |
:visited |
The <a> is clicked |
State | Element | Criteria |
---|---|---|
:enabled |
<button> |
(1) By default, a <button> is enabled. (2) <button enabled> |
:disabled |
<button> |
<button disabled> |
:checked |
<input type="checkbox"> |
(1) User ticks a checkbox. (2) <input type="checkbox" checked> |
:indeterminate |
<input type="checkbox"> |
<input type="checkbox" indeterminate> |
:valid |
<input type="email|number|..."> |
The input matches its type. |
:invalid |
<input type="email|number|..."> |
The input does not match its type. |
:in-range |
<input type="number" min="x" max="y"> |
The input is large equal than x and less equal than y. |
:required |
<input required> |
The <input> with the required attribute. |
:optional |
<input> |
(1) By default, an <input> is optional. (2) <input optional> |
:first-child
,:nth-child(n)
and:last-child
find the nth of child element. These selectors must follow the child element, not the parent.
.container p:first-child {
color: red;
}
.container p:nth-child(3) {
color: green;
}
.container p:last-child {
color: blue;
}
:first-of-type
,:nth-of-type(n)
and:last-of-type
points to the nth element of a specific type. These selectors must follow the child element, not the parent.
.container p:first-of-type {
color: red;
}
.container p:nth-of-type(3) {
color: green;
}
.container p:last-of-type {
color: blue;
}
The above n can be even
to create a striped table. Alternatively, it can apply the An+B microsyntax[12] to find child elements at regularly spaced intervals.
<article>
<div>I amd a div</div>
<div></div>
<h2></h2>
<div></div>
<p>I am a paragraph</p>
<h3>I am a heading 3</h3>
<div></div>
<p></p>
<div>I amd a div</div>
</article>
article {
line-hight: 1em;
}
article > * + * {
margin-top: 0.5em;
}
:empty
finds empty elements. Noticeably, an empty element has no blank.
article :empty {
display: none;
}
:is([classes])
is an elegant way to update specific classes, replacing the above CSS:
article p,
article div {
color: green;
}
article :is(p, div) {
color: green;
}
:not([classes])
excludes classes.
article :not(div) {
text-decoration: underline;
}
A pseudo-element starts with double colons following with a selector, for example, p::first-line
and my-class::before
. Pseudo-elements add or target an extra element without having to add more HTML. All the pseudo-elements are listed below:
::before
::after
::first-letter
::first-line
::marker
::selection
::placeholder
::backdrop
::cue
Go to CodePen to how those pseudo-elements work.
For example, the HTML elements can have descendent combinators .top div p div p div p
. A space separates parent selectors and their child selectors.
<div class="top box">
<div>
<p>1 level deep</p>
<div>
<p>2 levels deep</p>
<div>
<p>3 levels deep</p>
</div>
</div>
</div>
</div>
.top div {
padding-left: 2em;
background: #fff;
border-top: 1px solid blue;
border-left: 1px solid blue;
}
The layout becomes as follows:
As descendent selectors are recursive, it affects all the children. That is why the following example has overlapped borders.
.top div {
border-bottom: 1px solid blue;
border-right: 1px solid blue;
}
We add a character between selectors to specify the items based on their position to address such issues. >
refers to the first descendent in the hierarchy.
.top > div {
border-bottom: 1px solid blue;
border-right: 1px solid blue;
}
The layout becomes as follows:
The syntax of sibling combinators is former_element + target_element { style properties }
[3]. For example,
/* Paragraphs that come immediately after any image */
img + p {
font-weight: bold;
}
Below is another example. It lexically refers to anything that comes after anything under the top
class. That implies all elements except for the first child.
.top * + * {
margin-top: 1.5em;
}
A general sibling combinator selects siblings that come anywhere after a given element. For example, we want to select all the p
tag after h1
in the given HTML below.
<article>
<p>A normal paragraph</p>
<div>
<h1>A heading</h1>
<p>I am a red paragraph.</p>
<div>I am a div</div>
<p>I am another red paragraph.</p>
</div>
<p>A normal paragraph</p>
</article>
h1 ~ p {
color: red;
}
- Selectors
- Pseudo-elements
- Pseudo-classes
- The An+B microsyntax
- CodePen: basic selectors
- CodePen: child combinators
- CodePen: sibling combinators
- CodePen: pseudo-elements
- CodePen: pseudo-classes
By using extrinsic sizing, there is a limit of how much child content can add before it overflows out of the parent's bounds. For example, adding width: 320px;
to a CSS component makes it extrinsically sized. The problem with the extrinsic sizing is that the child overflows outside of the parent's box if its content is too large for the parent.
.parent {
width: 400px;
height: 400px;
}
Adding width: min-content
to the parent tells the box only to be as wide as the intrinsic minimum width of its content so that the box can fit its content. Therefore, the browser determines the size of a box.
.parent {
width: min-content;
height: min-content;
}
min-content
is the smallest box size where its contents do not overflow outside the box. The max-content
sizing keyword represents the intrinsic maximum width or height of the content. For text content, this means that the content will not wrap at all, even if it causes overflows [1].
The image below shows relevant CSS properties:
Read MDN Web Docs carefully and compare box-sizing: content-box;
and box-sizing: border-box;
.
Cascade is an algorithm that solves conflicts where multiple CSS rules apply to an HTML element. The algorithm is split into four distinct stages: [4]
- Position and order of appearance
- Specificity
- Origin
- Importance
The colour of the button is blue in all cases below.
- Scenario 1: Two attributes in one selector.
button {
color: red;
color: blue;
}
- Scenario 2: Two selectors in order.
button {
color: red;
}
button {
color: blue;
}
- Scenario 3: Two links in order.
In your HTML file, you add two links.
<link rel="stylesheet" href="https://codepen.io/my-awesome-css/pen/button1.css" />
<link rel="stylesheet" href="https://codepen.io/my-awesome-css/pen/button2.css" />
In button1.css, you we have
button {
color: red;
}
In button2.css, you we have
button {
color: blue;
}
- Scenario 4: One link and one CSS selector
<link rel="stylesheet" href="./style.css" />
<link rel="stylesheet" href="https://codepen.io/my-awesome-css/pen/button2.css" />
In style.css, we have
button {
color: red;
}
In button2.css, we have
button {
color: blue;
}
<h1 id="my-id" class="my-element" data-type="heading">Heading</h1>
* {
color: yellow;
}
#my-id {
color: blue;
}
.my-element {
color: red;
}
[data-type='heading'] {
color: green;
}
h1 {
color: black;
}
The colour of the heading is blue, because the priority of CSS selectors are ID selector > class selector > type selector > attribute selector > universal selector.
Go to the Specificity section to learn more.
User agent styles are the browser's default styles. Local user styles are OS-level styles like font size or browser extensions that allow users to customise CSS for the current website. Authored CSS is a website's CSS.
A universal selector has no specificity.
* {
color: red;
}
div {
color: red;
}
::selection {
color: red;
}
.my-class {
color: red;
}
:hover {
color: red;
}
[href='#'] {
color: red;
}
#myID {
color: red;
}
<div style="color: red"></div>
.my-class {
color: red !important; /* 10,000 points */
background: white; /* 10 points */
}
The type selector div
gets 1 point and the pseudo-class selector not
gets 10 points. Hence, this sample has 11 points of specificity.
div:not(.my-class) {
color: red;
}
11 Points of specificity.
a[href="#"] {
color: red;
}
Given a HTML:
<a class="my-class another-class" href="#">A link</a>
/* Q1 */
a {
color: red;
}
/* Q2 */
a.my-class {
color: green;
}
/* Q3 */
a.my-class.another-class {
color: rebeccapurple;
}
/* Q4 */
a.my-class.another-class[href] {
color: goldenrod;
}
/* Q5 */
a.my-class.another-class[href]:hover {
color: lightgrey;
}
The specificity of each quiz is listed below:
- Q1: 1 (1 type selector)
- Q2: 11 (1 type selector + 1 class selector)
- Q3: 21 (1 type selector + 2 class selectors)
- Q4: 31 (1 type selector + 2 class selectors + 1 attribute selector)
- Q5: 41 (1 type selector + 2 class selectors + 1 attribute selector + 1 pseudo-class selector)
With the inherit
keyword, the child inherit CSS from its parent. Not all CSS properties are inheritable, check out Inheritable CSS Properties to review all inheritable CSS Properties.
Go to Font-size-relative units and Viewport-relative units to see all available relative font-size units and view-size units, respectively.
Inline elements behave like words in a sentence. They sit next to each other in the inline direction.
You can't set an explicit width and height on inline elements. Any block level margin and padding will be ignored by the surrounding elements [7].
Block elements don't sit alongside each other. They create a new line for themselves [7].
Flexbox is a layout mechanism designed for laying out groups of items in one dimension [8].
Given a parent component and three children as follows:
<div class="content">
<p>Paragraph A</p>
<p>Paragraph B</p>
<p>Paragraph C</p>
</div>
.content {
display: flex;
}
The three paragraphs are arranged in a horizontal row.
| Paragraph A | Paragraph B | Paragraph C |
Children components can be arranged in a vertical column by setting the flex-direction
property as column
.
.content {
display: flex;
flex-direction: column;
}
| Paragraph A |
| Paragraph B |
| Paragraph C |
The main axis is the direction set by the flex-direction property. The cross axis runs in the other direction to the main axis. For example, if the main axis is column
, the cross axis runs along the row
.
By default, the flex direction of flexbox is nowrap
. If there is not enough space in the container, the items will overflow.
.container {
display: flex;
flex-wrap: nowrap;
}
Flex items can be squeezed to the new line to next line depending on the flex direction and the space of its container.
.container {
display: flex;
flex-wrap: wrap;
}
The shorthand flex-flow
is a combination of flex-direction
and flex-wrap
. For example, the following two piece of CSS are equivalent.
.container {
display: flex;
flex-direction: column;
flex-wrap: wrap;
}
.container {
display: flex;
flex-flow: column wrap;
}
Use the flex
or flex-
properties to control the space inside flex items. For example,
.container {
display: flex;
gap: 1em;
}
.item:first-child {
flex: 3;
}
.item:nth-child(2) {
flex: 1;
}
.item:nth-child(3) {
flex: 2;
}
Go to Controlling space inside flex items to read more properties with the flex-
prefix.
You can order flex items by adding the order
property. Notice, no need to add order: 1
.
.container {
display: flex;
gap: 1em;
}
.item:first-child {
order: 3;
}
.item:nth-child(2) {
/* no order: 1; */
}
.item:nth-child(3) {
order: 2;
}
You will get
item 2 | item 3 | item 1
justify-content
: space distribution on the main axis (the value of your flex-direction).align-content
: space distribution on the cross axis.place-content
: a shorthand for setting both of the above properties.
Go to Flexbox alignment overview to read more.
Go to Grid terminology to learn what lines, tracks, cells, areas and gaps are.
There are five use cases for the grid. Each example provides the most significant part of the code. Source code is available at CodePen.
- Draw a grid with 3-column tracks and 2-row tracks. Each cell has a different size.
<div class="container simple-grid">
<div class="box">5em * 120px</div>
<div class="box">120px * 120px</div>
<div class="box">30% * 120px</div>
<div class="box">5em * auto</div>
<div class="box">120px * auto</div>
</div>
.container {
display: grid;
gap: 8px;
}
.simple-grid {
grid-template-columns: 5em 120px 30%;
grid-template-rows: 120px auto;
}
- Draw an intrinsic sizing grid with 3-column tracks and 2-row tracks, and make sure its track content will not overflow.
- The
fit-content(size)
property fits the content and place child items evenly. You will needfit-content(size)
. - The
auto
property stretches items automatically to fit the content. min-content
andmax-content
properties ensure the child item has a minimum size and enough space without squeeze content, respectively, even though the items may overflow.
- The
<div class="container intrinsic-sizing">
<div class="box">Box</div>
<div class="box">Box</div>
<div class="box">Box</div>
<div class="box">Box</div>
<div class="box">Box</div>
</div>
.intrinsic-sizing {
grid-template-columns: repeat(3, min-content);
grid-template-rows: 120px auto;
}
/*
Other available intrinsic sizing options:
grid-template-columns: repeat(3, min-content);
grid-template-columns: repeat(3, max-content);
grid-template-columns: repeat(3, fit-content(5em));
grid-template-columns: repeat(3, auto);
*/
- Draw a grid with 3-column tracks and 2-row tracks. The width ratio of items in a row should be 1:3:2.
.fr {
grid-template-columns: 1fr 3fr 2fr;
grid-template-rows: 120px auto;
}
fr
is a special unit used in gird.
- Draw a grid with 3-column tracks and 2-row tracks. The last item in a row has the maximum size
2fr
. With theminmax()
, the item can be stretched automatically while there is enough space.
.min-max {
grid-template-columns: 1fr 2fr minmax(auto, 2fr);
grid-template-rows: 120px auto;
}
- Draw a grid with 5-column tracks and 3-row tracks. Each child item has the same size.
<div class="container repeat">
<div class="box">Box</div>
<div class="box">Box</div>
<div class="box">Box</div>
<div class="box">Box</div>
<div class="box">Box</div>
<div class="box">Box</div>
<div class="box">Box</div>
<div class="box">Box</div>
<div class="box">Box</div>
<div class="box">Box</div>
<div class="box">Box</div>
</div>
- Given a fixed-width grid, try to adjust the items automatically. There are two scenarios:
.auto-fill {
width: 100%;
grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
grid-template-rows: 120px 80px;
}
.auto-fit {
width: 100%;
grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
grid-template-rows: 120px 80px;
}
- Instead of placing items along the rows, drawing a grid layout places items into columns.
.column-flow {
grid-auto-flow: column;
grid-template-columns: 120px 80px;
grid-template-rows: repeat(3, 1fr);
writing-mode: horizontal-tb;
}
The writing mode is the direction a row runs. There is a multitude of options listed on MDN Web Docs. Some CSS properties come with vertical written mode relative to the direction of their sides. For example,
<div class="wrapper">
<article class="container">
<div class="box rotate">box</div>
</div>
.container {
border: 1px solid blue;
width: max-content;
}
.rotate {
writing-mode: vertical-rl;
margin-top: 1em; /* absolute position */
margin-block-end: 1em; /* relative position */
min-block-size: 100px; /* width */
min-inline-size: 150px;
}
margin-top
is applied to the physical top of the element, i.e., margin-top
has nothing to do with the writing-mode
. margin-block-end
, on the other hand, rests on the direction of the writing mode. So if the item is placed right to left vertically, the margin-block-end
works on the right-hand side of the item physically. It affects the left-hand side when the item is placed left to right.
- Merge items horizontally and/or vertically.
For example, we create a 4 * 5 grid.
<div class="container fixed-cells">
<div class="box heading">Box1</div>
<div class="box menu">Box2</div>
<div class="box sidemenu">Box3</div>
<div class="box main">Box4</div>
<div class="box footer">Box5</div>
</div>
.fixed-cells {
grid-template-columns: repeat(4, auto);
grid-template-rows: repeat(5, 80px);
}
.heading {
grid-column-start: 1;
grid-column-end: 4;
}
.menu {
grid-column-start: 4;
grid-column-end: 5;
}
.sidemenu {
grid-column-start: 1;
grid-column-end: 2;
grid-row-start: 2;
grid-row-end: 5;
}
.main {
grid-column-start: 2;
grid-column-end: 5;
grid-row-start: 2;
grid-row-end: 5;
}
.footer {
grid-column-start: 1;
grid-column-end: 5;
}
Notice, grid columns start from index 1. The property grid-column-start: 2
and grid-column-end: 3
can be merged into grid-column: 2 / 3
.
The margin
property can be a percentage based on the width of the element's containing block. For example, a 240px * 300px container contains an element with margin: 10%
, the margin of of that element is 30px (The width of the container is 300px).
inline-margin
and inline-size
work in the relatively-horizontal direction. block-margin
and block-size
affect the relatively-vertical direction—their direction changes along with the writing-mode
.
The actual margin between the following items is 32px, not 48px, because of margin collapses. Only block margins collapse, not inline (horizontal) margins.
Use padding
to create space inside a box. Use margin
to create space outside a box. Lastly, use gap
to create space between boxes inside of a grid or flexbox container. The box-sizing
property determines how the total width and height of an element is calculated. Check out MDN box sizing to read more.
Z-index works with a position
property other than static
.
A CSS function is a pure function. CSS functions can be categorised into a few groups.
var(variable, default)
allows properties to read user-defined variables. The default value is optional.calc(expression)
makes mathematical calculation and returns a number.min(size1, size2)
,max(size1, size2)
,clamp(min, ideal size, max)
return a number depending on the condition. For example,width: min(50px, 10%);
sets the width of an element to 50px when the minimum width of its container is greater than 50px. Otherwise, the width will be 10% of the width of the container.- Shapes: Functions like
circle()
,ellipse()
,inset()
andpolygon()
are used to clip a box or provide a shape for content to flow around. For example, CSS clip-path maker is a great tool to clip a shape. - Transforms: Transform functions, which skew, resize and even change the depth of an element.
Keyframes are the mechanism that you use to assign animation states to timestamps, along a timeline [13]. Below is how keyframe is implemented:
@keyframes custom-ident {
timeline-before {
/* do something */
}
timeline-after {
/* do something */
}
}
The custom-ident is the name of a keyframe. That identifier is case-sensitive. The timeline-before
and timeline-after
represent the start and end of an animation, respectively. They could be a from
and to
pair or a percentage pair like 0%
and 100%
.
With the animation (keyframe), we can create an animation with other properties such as
animation-duration
: defines how long the keyframe timeline should be.animation-timing-function
: calculates the speed of the animation at each point.animation-iteration-count
: defines how many times the @keyframes timeline should run.animation-direction
: defines which direction the timeline runs over your keyframes.animation-delay
: defines how long to wait before starting the animation.animation-play-state
: The default value isrunning
. This property can pause an animation by setting upanimation-play-state: paused
.animation-fill-mode
The shorthand of the animation CSS is `animation: name duration timing-function delay iterator-count direction fill-mode play-state.
Users can define in their operating system that they prefer to reduce motion experienced when interacting with applications and websites.
@media (prefers-reduced-motion) {
.my-autoplaying-animation {
animation-play-state: paused;
}
}