feat(curriculum): add book inventory lab (#55678)
parent
b9893bb4d6
commit
90b2cfbfeb
|
@ -2087,9 +2087,9 @@
|
|||
"title": "94",
|
||||
"intro": []
|
||||
},
|
||||
"95": {
|
||||
"title": "95",
|
||||
"intro": []
|
||||
"lab-book-inventory-app": {
|
||||
"title": "Build a Book Inventory App",
|
||||
"intro": ["For this lab, you will create a book inventory app."]
|
||||
},
|
||||
"96": {
|
||||
"title": "96",
|
||||
|
@ -2634,8 +2634,8 @@
|
|||
"title": "230",
|
||||
"intro": []
|
||||
},
|
||||
"230": {
|
||||
"title": "230",
|
||||
"231": {
|
||||
"title": "231",
|
||||
"intro": []
|
||||
},
|
||||
"232": {
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
title: Introduction to the Build a Book Inventory App
|
||||
block: lab-book-inventory-app
|
||||
superBlock: front-end-development
|
||||
---
|
||||
|
||||
## Introduction to the Build a Book Inventory App
|
||||
|
||||
For this lab, you will create a book inventory app.
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"name": "Build a Book Inventory App",
|
||||
"blockType": "lab",
|
||||
"isUpcomingChange": true,
|
||||
"usesMultifileEditor": true,
|
||||
"dashedName": "lab-book-inventory-app",
|
||||
"order": 95,
|
||||
"superBlock": "front-end-development",
|
||||
"challengeOrder": [{ "id": "66a207974c806a19d6607073", "title": "Build a Book Inventory App" }],
|
||||
"helpCategory": "HTML-CSS"
|
||||
}
|
|
@ -0,0 +1,635 @@
|
|||
---
|
||||
id: 66a207974c806a19d6607073
|
||||
title: Build a Book Inventory App
|
||||
challengeType: 14
|
||||
dashedName: build-a-book-inventory-app
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
|
||||
**Objective:** Fulfill the user stories below and get all the tests to pass to complete the lab.
|
||||
|
||||
**User Stories:**
|
||||
|
||||
1. You should have an `h1` element with the text `Book Inventory`.
|
||||
1. You should have a `table` element with columns named `Title`, `Author`, `Category`, `Status`, and `Rate`.
|
||||
1. Your table should have at least four rows, the first for the column headings and the rest filled with book information.
|
||||
1. Each table row inside the table body should have either the class `read`, `to-read`, or `in-progress`.
|
||||
1. `td` elements of the `Status` column should contain a `span` element with the `class` of `status` surrounding the text `Read`, `To Read`, or `In Progress`, depending on the class of that row.
|
||||
1. `td` elements of the `Rate` column should contain a `span` element with the `class` of `rate` wrapping three empty `span` elements.
|
||||
1. `.rate` elements placed inside `.read` rows should have an additional class with the value of either `one`, `two`, or `three`, depending on the personal rate. This value should come after `rate`.
|
||||
1. You should create three attribute selectors to target the elements with the class of `read`, `to-read`, and `in-progress`, and set their `background-image` property to use a `linear-gradient` of your choice.
|
||||
1. You should set the `display` property of each `span` element to `inline-block`.
|
||||
1. You should use an attribute selector to target the `span` elements with the class of `status` that are descendants of `tr` elements with the class of `to-read` and set their `border` and `background-image` properties.
|
||||
1. You should use an attribute selector to target the `span` elements with the class of `status` that are descendants of `tr` elements with the class of `read` and set their `border` and `background-image` properties.
|
||||
1. You should use an attribute selector to target the `span` elements with the class of `status` that are descendants of `tr` elements with the class of `in-progress` and set their `border` and `background-image` properties.
|
||||
1. You should use an attribute selector to target the `span` elements with the class of `status` and the `span` elements with the class value starting with `rate` and set their `height`, `width`, and `padding` properties.
|
||||
1. You should use an attribute selector to target the `span` elements which are direct children of `span` elements with the `class` value starting with `rate` and set their `border`, `border-radius`, `margin`, `height`, `width`, and `background-color` properties.
|
||||
1. You should use an attribute selector to target the first descendant of `span` elements that have `one` as a part of their `class` value and set its `background-image` property to use a `linear-gradient`.
|
||||
1. You should use an attribute selector to target the first two descendants of `span` elements that have `two` as a part of their `class` value and set their `background-image` property to use a `linear-gradient`.
|
||||
1. You should use an attribute selector to target the three `span` elements that are descendants of `span` elements that have `three` as a part of their `class` value and set their `background-image` property to use a `linear-gradient`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should have an `h1` element with the text `Book Inventory`.
|
||||
|
||||
```js
|
||||
assert.equal(document.querySelector('h1')?.innerText, 'Book Inventory');
|
||||
```
|
||||
|
||||
You should have only one `h1` element.
|
||||
|
||||
```js
|
||||
assert.equal(document.querySelectorAll('h1')?.length, 1);
|
||||
```
|
||||
|
||||
You should have a `table` element after your `h1` element.
|
||||
|
||||
```js
|
||||
assert.equal(document.querySelector('table')?.previousElementSibling?.tagName, 'H1')
|
||||
```
|
||||
|
||||
Your `table` element should have five columns.
|
||||
|
||||
```js
|
||||
assert.equal(document.querySelectorAll('th')?.length, 5);
|
||||
```
|
||||
|
||||
Your first column should have the text `Title` as the heading.
|
||||
|
||||
```js
|
||||
assert.equal(document.querySelectorAll('th')[0]?.innerText, 'Title');
|
||||
```
|
||||
|
||||
Your second column should have the text `Author` as the heading.
|
||||
|
||||
```js
|
||||
assert.equal(document.querySelectorAll('th')[1]?.innerText, 'Author');
|
||||
```
|
||||
|
||||
Your third column should have the text `Category` as the heading.
|
||||
|
||||
```js
|
||||
assert.equal(document.querySelectorAll('th')[2]?.innerText, 'Category');
|
||||
```
|
||||
|
||||
Your fourth column should have the text `Status` as the heading.
|
||||
|
||||
```js
|
||||
assert.equal(document.querySelectorAll('th')[3]?.innerText, 'Status');
|
||||
```
|
||||
|
||||
Your fifth column should have the text `Rate` as the heading.
|
||||
|
||||
```js
|
||||
assert.equal(document.querySelectorAll('th')[4]?.innerText, 'Rate');
|
||||
```
|
||||
|
||||
Your table should have at least four rows.
|
||||
|
||||
```js
|
||||
const rows = document.querySelectorAll('tr');
|
||||
assert.isAtLeast(rows.length, 4);
|
||||
```
|
||||
|
||||
Each table row except the heading row should have either the class `read`, `to-read`, or `in-progress`.
|
||||
|
||||
```js
|
||||
const rows = document.querySelectorAll('tr');
|
||||
assert.isAtLeast(rows.length, 4);
|
||||
for (let i = 1; i < rows.length; i++) {
|
||||
assert(
|
||||
(
|
||||
rows[i].classList).contains('read')
|
||||
|| (rows[i].classList).contains('to-read')
|
||||
|| (rows[i].classList).contains('in-progress')
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
`td` elements of the `Status` column should contain a `span` element.
|
||||
|
||||
```js
|
||||
const statusColumnData = document.querySelectorAll('td:nth-child(4)');
|
||||
assert.isAbove(statusColumnData.length, 0);
|
||||
for (let e of statusColumnData) {
|
||||
assert.equal(e?.children[0]?.tagName, 'SPAN');
|
||||
}
|
||||
```
|
||||
|
||||
Each `span` element of the `Status` column should have the class of `status`.
|
||||
|
||||
```js
|
||||
const statusSpans = document.querySelectorAll('tr td:nth-child(4) :first-child');
|
||||
assert.isAbove(statusSpans.length, 0);
|
||||
for (let e of statusSpans) {
|
||||
assert(e.classList.contains('status'));
|
||||
}
|
||||
```
|
||||
|
||||
Each `.status` element should have the text `Read`, `To Read`, or `In Progress`, depending on the class of its row.
|
||||
|
||||
```js
|
||||
const statusSpans = document.querySelectorAll('tr td:nth-child(4) :first-child');
|
||||
const rows = Array.from(document.querySelectorAll('tr')).slice(1);
|
||||
assert.isAbove(statusSpans.length, 0);
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
switch (statusSpans[i]?.innerText) {
|
||||
case 'Read':
|
||||
assert(rows[i].classList.contains('read'));
|
||||
break;
|
||||
case 'To Read':
|
||||
assert(rows[i].classList.contains('to-read'));
|
||||
break;
|
||||
case 'In Progress':
|
||||
assert(rows[i].classList.contains('in-progress'));
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`td` elements of the `Rate` column should contain a `span` element.
|
||||
|
||||
```js
|
||||
const rateColumnData = document.querySelectorAll('tr td:last-child');
|
||||
assert.isAbove(rateColumnData.length, 0);
|
||||
for (let e of rateColumnData) {
|
||||
assert.equal(e.children[0]?.tagName, 'SPAN')
|
||||
}
|
||||
```
|
||||
|
||||
Each `span` element which is a direct child of a `td` element of the `Rate` column should have the class of `rate` as the first class.
|
||||
|
||||
```js
|
||||
const rateSpans = document.querySelectorAll('tr td:last-child > span:first-child');
|
||||
assert.isAbove(rateSpans.length, 0);
|
||||
for (let e of rateSpans) {
|
||||
assert.equal(e.classList[0], 'rate');
|
||||
}
|
||||
```
|
||||
|
||||
Each `.rate` element should contain three empty `span` elements.
|
||||
|
||||
```js
|
||||
const rateSpans = document.getElementsByClassName('rate');
|
||||
assert.isAbove(rateSpans.length, 0);
|
||||
for (let e of rateSpans) {
|
||||
assert.equal(e.children.length, 3);
|
||||
for (let child of e.children) {
|
||||
assert.equal(child.tagName, 'SPAN');
|
||||
assert.equal(child.innerText.length, 0);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`.rate` elements placed inside `.read` rows should have an additional class after the `rate` class with the value of either `one`, `two`, or `three`, depending on the personal rate.
|
||||
|
||||
```js
|
||||
const readBooksRates = document.querySelectorAll('.read .rate');
|
||||
assert.isAbove(readBooksRates.length, 0);
|
||||
for (let e of readBooksRates) {
|
||||
assert(['one', 'two', 'three'].includes(e.classList[1]));
|
||||
}
|
||||
```
|
||||
|
||||
You should have an attribute selector to target rows that have the class of `read`.
|
||||
|
||||
```js
|
||||
assert.exists(new __helpers.CSSHelp(document).getStyle('tr[class="read"]'));
|
||||
```
|
||||
|
||||
You should use an attribute selector to target rows that have the class of `read` and set their `background-image` property to a linear gradient of your choice.
|
||||
|
||||
```js
|
||||
assert.include(new __helpers.CSSHelp(document).getStyle('tr[class="read"]')?.backgroundImage, 'linear-gradient(');
|
||||
```
|
||||
|
||||
You should have an attribute selector to target rows that have the class of `to-read`.
|
||||
|
||||
```js
|
||||
assert.exists(new __helpers.CSSHelp(document).getStyle('tr[class="to-read"]'));
|
||||
```
|
||||
|
||||
You should use an attribute selector to target rows that have the class of `to-read` and set their `background-image` property to a linear gradient of your choice.
|
||||
|
||||
```js
|
||||
assert.include(new __helpers.CSSHelp(document).getStyle('tr[class="to-read"]')?.backgroundImage, 'linear-gradient(');
|
||||
```
|
||||
|
||||
You should have an attribute selector to target rows that have the class of `in-progress`.
|
||||
|
||||
```js
|
||||
assert.exists(new __helpers.CSSHelp(document).getStyle('tr[class="in-progress"]'));
|
||||
```
|
||||
|
||||
You should use an attribute selector to target rows that have the class of `in-progress` and set their `background-image` property to a linear gradient of your choice.
|
||||
|
||||
```js
|
||||
assert.include(new __helpers.CSSHelp(document).getStyle('tr[class="in-progress"]')?.backgroundImage, 'linear-gradient(');
|
||||
```
|
||||
|
||||
You should set the `display` property of each `span` element to `inline-block`.
|
||||
|
||||
```js
|
||||
assert.equal(new __helpers.CSSHelp(document).getStyle('span')?.getPropVal('display'), 'inline-block');
|
||||
```
|
||||
|
||||
You should have an attribute selector to target the `span` elements with the class of `status` that are descendants of `tr` elements with the class of `to-read`.
|
||||
|
||||
```js
|
||||
assert.exists(new __helpers.CSSHelp(document).getStyle('tr[class="to-read"] span[class="status"]'));
|
||||
```
|
||||
|
||||
You should use an attribute selector to target the `span` elements with the class of `status` that are descendants of `tr` elements with the class of `to-read` and set their `border` property.
|
||||
|
||||
```js
|
||||
assert(new __helpers.CSSHelp(document).getStyle('tr[class="to-read"] span[class="status"]')?.border);
|
||||
```
|
||||
|
||||
You should use an attribute selector to target the `span` elements with the class of `status` that are descendants of `tr` elements with the class of `to-read` and set their `background-image` property.
|
||||
|
||||
```js
|
||||
assert(new __helpers.CSSHelp(document).getStyle('tr[class="to-read"] span[class="status"]')?.backgroundImage);
|
||||
```
|
||||
|
||||
You should have an attribute selector to target the `span` elements with the class of `status` that are descendants of `tr` elements with the class of `read`.
|
||||
|
||||
```js
|
||||
assert.exists(new __helpers.CSSHelp(document).getStyle('tr[class="read"] span[class="status"]'));
|
||||
```
|
||||
|
||||
You should use an attribute selector to target the `span` elements with the class of `status` that are descendants of `tr` elements with the class of `read` and set their `border` property.
|
||||
|
||||
```js
|
||||
assert(new __helpers.CSSHelp(document).getStyle('tr[class="read"] span[class="status"]')?.border);
|
||||
```
|
||||
|
||||
You should use an attribute selector to target the `span` elements with the class of `status` that are descendants of `tr` elements with the class of `read` and set their `background-image` property.
|
||||
|
||||
```js
|
||||
assert(new __helpers.CSSHelp(document).getStyle('tr[class="read"] span[class="status"]')?.backgroundImage);
|
||||
```
|
||||
|
||||
You should have an attribute selector to target the `span` elements with the class of `status` that are descendants of `tr` elements with the class of `in-progress`.
|
||||
|
||||
```js
|
||||
assert.exists(new __helpers.CSSHelp(document).getStyle('tr[class="in-progress"] span[class="status"]'));
|
||||
```
|
||||
|
||||
You should use an attribute selector to target the `span` elements with the class of `status` that are descendants of `tr` elements with the class of `in-progress` and set their `border` property.
|
||||
|
||||
```js
|
||||
assert(new __helpers.CSSHelp(document).getStyle('tr[class="in-progress"] span[class="status"]')?.border);
|
||||
```
|
||||
|
||||
You should use an attribute selector to target the `span` elements with the class of `status` that are descendants of `tr` elements with the class of `in-progress` and set their `background-image` property.
|
||||
|
||||
```js
|
||||
assert(new __helpers.CSSHelp(document).getStyle('tr[class="in-progress"] span[class="status"]')?.backgroundImage);
|
||||
```
|
||||
|
||||
You should have an attribute selector to target the `span` elements with the class of `status` and the `span` elements with the class value starting with `rate`.
|
||||
|
||||
```js
|
||||
const selector1 = new __helpers.CSSHelp(document).getStyle('span[class="status"], span[class^="rate"]');
|
||||
const selector2 = new __helpers.CSSHelp(document).getStyle('span[class^="rate"], span[class="status"]');
|
||||
assert.exists(selector1 || selector2);
|
||||
```
|
||||
|
||||
You should use an attribute selector to target the `span` elements with the class of `status` and the `span` elements with the class value starting with `rate` and set their `height` property.
|
||||
|
||||
```js
|
||||
const selector1 = new __helpers.CSSHelp(document).getStyle('span[class="status"], span[class^="rate"]');
|
||||
const selector2 = new __helpers.CSSHelp(document).getStyle('span[class^="rate"], span[class="status"]');
|
||||
assert(selector1?.height || selector2?.height);
|
||||
```
|
||||
|
||||
You should use an attribute selector to target the `span` elements with the class of `status` and the `span` elements with the class value starting with `rate` and set their `width` property.
|
||||
|
||||
```js
|
||||
const selector1 = new __helpers.CSSHelp(document).getStyle('span[class="status"], span[class^="rate"]');
|
||||
const selector2 = new __helpers.CSSHelp(document).getStyle('span[class^="rate"], span[class="status"]');
|
||||
assert(selector1?.width || selector2?.width);
|
||||
```
|
||||
|
||||
You should use an attribute selector to target the `span` elements with the class of `status` and the `span` elements with the class value starting with `rate` and set their `padding` property.
|
||||
|
||||
```js
|
||||
const selector1 = new __helpers.CSSHelp(document).getStyle('span[class="status"], span[class^="rate"]');
|
||||
const selector2 = new __helpers.CSSHelp(document).getStyle('span[class^="rate"], span[class="status"]');
|
||||
assert(selector1?.padding || selector2?.padding);
|
||||
```
|
||||
|
||||
You should have an attribute selector to target the `span` elements which are direct children of `span` elements with the `class` value starting with `rate`.
|
||||
|
||||
```js
|
||||
assert.exists(new __helpers.CSSHelp(document).getStyle('span[class^="rate"] > span'));
|
||||
```
|
||||
|
||||
You should use an attribute selector to target the `span` elements which are direct children of `span` elements with the `class` value starting with `rate` and set their `border` property.
|
||||
|
||||
```js
|
||||
assert(new __helpers.CSSHelp(document).getStyle('span[class^="rate"] > span')?.getPropVal('border'));
|
||||
```
|
||||
|
||||
You should use an attribute selector to target the `span` elements which are direct children of `span` elements with the `class` value starting with `rate` and set their `border-radius` property.
|
||||
|
||||
```js
|
||||
assert(new __helpers.CSSHelp(document).getStyle('span[class^="rate"] > span')?.getPropVal('border-radius'));
|
||||
```
|
||||
|
||||
You should use an attribute selector to target the `span` elements which are direct children of `span` elements with the `class` value starting with `rate` and set their `margin` property.
|
||||
|
||||
```js
|
||||
assert(new __helpers.CSSHelp(document).getStyle('span[class^="rate"] > span')?.getPropVal('margin'));
|
||||
```
|
||||
|
||||
You should use an attribute selector to target the `span` elements which are direct children of `span` elements with the `class` value starting with `rate` and set their `height` property.
|
||||
|
||||
```js
|
||||
assert(new __helpers.CSSHelp(document).getStyle('span[class^="rate"] > span')?.getPropVal('height'));
|
||||
```
|
||||
|
||||
You should use an attribute selector to target the `span` elements which are direct children of `span` elements with the `class` value starting with `rate` and set their `width` property.
|
||||
|
||||
```js
|
||||
assert(new __helpers.CSSHelp(document).getStyle('span[class^="rate"] > span')?.getPropVal('width'));
|
||||
```
|
||||
|
||||
You should use an attribute selector to target the `span` elements which are direct children of `span` elements with the `class` value starting with `rate` and set their `background-color` property.
|
||||
|
||||
```js
|
||||
assert(new __helpers.CSSHelp(document).getStyle('span[class^="rate"] > span')?.getPropVal('background-color'));
|
||||
```
|
||||
|
||||
You should have an attribute selector to target the first descendant of `span` elements that have `one` as a part of their `class` value.
|
||||
|
||||
```js
|
||||
const selector1 = new __helpers.CSSHelp(document).getStyle('span[class~="one"] :first-child');
|
||||
const selector2 = new __helpers.CSSHelp(document).getStyle('span[class~="one"] :nth-child(1)');
|
||||
assert.exists(selector1 || selector2);
|
||||
```
|
||||
|
||||
You should use an attribute selector to target the first descendant of `span` elements that have `one` as a part of their `class` value and set its `background-image` property to use a `linear-gradient`.
|
||||
|
||||
```js
|
||||
const selector1 = new __helpers.CSSHelp(document).getStyle('span[class~="one"] :first-child');
|
||||
const selector2 = new __helpers.CSSHelp(document).getStyle('span[class~="one"] :nth-child(1)');
|
||||
assert(selector1?.getPropVal('background-image').includes('linear-gradient(') || selector2?.getPropVal('background-image').includes('linear-gradient('));
|
||||
```
|
||||
|
||||
You should have an attribute selector to target the first two descendants of `span` elements that have `two` as a part of their `class` value.
|
||||
|
||||
```js
|
||||
const selector1 = new __helpers.CSSHelp(document).getStyle('span[class~="two"] :nth-child(1), span[class~="two"] :nth-child(2)');
|
||||
const selector2 = new __helpers.CSSHelp(document).getStyle('span[class~="two"] :nth-child(2), span[class~="two"] :nth-child(1)');
|
||||
const selector3 = new __helpers.CSSHelp(document).getStyle('span[class~="two"] :first-child, span[class~="two"] :nth-child(2)');
|
||||
const selector4 = new __helpers.CSSHelp(document).getStyle('span[class~="two"] :nth-child(2), span[class~="two"] :first-child');
|
||||
assert.exists(selector1 || selector2 || selector3 || selector4);
|
||||
```
|
||||
|
||||
You should use an attribute selector to target the first two descendants of `span` elements that have `two` as a part of their `class` value and set their `background-image` property to use a `linear-gradient`.
|
||||
|
||||
```js
|
||||
const selector1 = new __helpers.CSSHelp(document).getStyle('span[class~="two"] :nth-child(1), span[class~="two"] :nth-child(2)');
|
||||
const selector2 = new __helpers.CSSHelp(document).getStyle('span[class~="two"] :nth-child(2), span[class~="two"] :nth-child(1)');
|
||||
const selector3 = new __helpers.CSSHelp(document).getStyle('span[class~="two"] :first-child, span[class~="two"] :nth-child(2)');
|
||||
const selector4 = new __helpers.CSSHelp(document).getStyle('span[class~="two"] :nth-child(2), span[class~="two"] :first-child');
|
||||
const selectors = [selector1, selector2, selector3, selector4];
|
||||
let isTrue = false;
|
||||
for (let selector of selectors) {
|
||||
if (selector?.backgroundImage.includes('linear-gradient(')) {
|
||||
isTrue = true;
|
||||
}
|
||||
}
|
||||
assert(isTrue);
|
||||
```
|
||||
|
||||
You should have an attribute selector to target the `span` elements that are descendants of `span` elements that have `three` as a part of their `class` value.
|
||||
|
||||
```js
|
||||
assert.exists(new __helpers.CSSHelp(document).getStyle('span[class~="three"] span'));
|
||||
```
|
||||
|
||||
You should use an attribute selector to target the `span` elements that are descendants of `span` elements that have `three` as a part of their `class` value and set their `background-image` property to use a `linear-gradient`.
|
||||
|
||||
```js
|
||||
assert.include(new __helpers.CSSHelp(document).getStyle('span[class~="three"] span')?.getPropVal('background-image'), 'linear-gradient(');
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Book Inventory</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Book Inventory</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main>
|
||||
<h1>Book Inventory</h1>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Author</th>
|
||||
<th>Category</th>
|
||||
<th>Status</th>
|
||||
<th>Rate</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="read">
|
||||
<td>The Three Musketeers</td>
|
||||
<td>Alexandre Dumas</td>
|
||||
<td>Historical Novel</td>
|
||||
<td><span class="status">Read</span></td>
|
||||
<td>
|
||||
<span class="rate three">
|
||||
<span></span><span></span><span></span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="to-read">
|
||||
<td>The Plague</td>
|
||||
<td>Albert Camus</td>
|
||||
<td>Philosofical Novel</td>
|
||||
<td><span class="status">To Read</span></td>
|
||||
<td>
|
||||
<span class="rate">
|
||||
<span></span><span></span><span></span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="read">
|
||||
<td>The Metamorphosis</td>
|
||||
<td>Franz Kafka</td>
|
||||
<td>Novella</td>
|
||||
<td><span class="status">Read</span></td>
|
||||
<td>
|
||||
<span class="rate one">
|
||||
<span></span><span></span><span></span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="read">
|
||||
<td>Dead Souls</td>
|
||||
<td>Nikolai Gogol</td>
|
||||
<td>Picaresque</td>
|
||||
<td><span class="status">Read</span></td>
|
||||
<td>
|
||||
<span class="rate two">
|
||||
<span></span><span></span><span></span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="in-progress">
|
||||
<td>Lord of the Flies</td>
|
||||
<td>William Golding</td>
|
||||
<td>Allegorical Novel</td>
|
||||
<td><span class="status">In Progress</span></td>
|
||||
<td>
|
||||
<span class="rate">
|
||||
<span></span><span></span><span></span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="read">
|
||||
<td>Do Androids Dream of Electric Sheep?</td>
|
||||
<td>Philip K. Dick</td>
|
||||
<td>Science Fiction</td>
|
||||
<td><span class="status">Read</span></td>
|
||||
<td>
|
||||
<span class="rate two">
|
||||
<span></span><span></span><span></span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
thead {
|
||||
background-image: linear-gradient(hsl(197, 92%, 77%, 0.60), hsl(197, 92%, 50%, 0.60));
|
||||
}
|
||||
|
||||
th {
|
||||
padding: 0.4em;
|
||||
}
|
||||
|
||||
td {
|
||||
text-align: center;
|
||||
padding: 0.3em;
|
||||
}
|
||||
|
||||
tr[class="read"] {
|
||||
background-image: linear-gradient(hsl(120, 100%, 85%, 0.6), hsl(120, 100%, 65%, 0.6));
|
||||
}
|
||||
|
||||
tr[class="to-read"] {
|
||||
background-image: linear-gradient(hsl(120, 20%, 95%, 0.6), hsl(120, 20%, 75%, 0.6));
|
||||
}
|
||||
|
||||
tr[class="in-progress"] {
|
||||
background-image: linear-gradient(hsl(40, 100%, 85%, 0.60), hsl(40, 100%, 65%, 0.6));
|
||||
}
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
span[class="status"] {
|
||||
border-radius: 20%/60%;
|
||||
}
|
||||
|
||||
span[class="status"],
|
||||
span[class^="rate"] {
|
||||
height: 1.85em;
|
||||
width: 7em;
|
||||
padding: 0.3em;
|
||||
}
|
||||
|
||||
tr[class="to-read"] span[class="status"] {
|
||||
border: 0.1em solid hsl(5, 100%, 45%);
|
||||
background-image: linear-gradient(hsl(5, 100%, 75%), hsl(5, 100%, 50%));
|
||||
}
|
||||
|
||||
tr[class="read"] span[class="status"] {
|
||||
border: 0.1em solid hsl(120, 75%, 45%);
|
||||
background-image: linear-gradient(hsl(120, 75%, 75%), hsl(120, 75%, 50%));
|
||||
}
|
||||
|
||||
tr[class="in-progress"] span[class="status"] {
|
||||
border: 0.1em solid hsl(40, 90%, 40%);
|
||||
background-image: linear-gradient(hsl(40, 90%, 75%), hsl(40, 90%, 50%));
|
||||
}
|
||||
|
||||
span[class^="rate"] > span {
|
||||
border: 0.1em solid hsl(0, 0%, 50%);
|
||||
border-radius: 50%;
|
||||
background-color: hsl(0, 15%, 95%);
|
||||
height: 100%;
|
||||
width: 20%;
|
||||
margin: 0.1em;
|
||||
}
|
||||
|
||||
span[class~="three"] span {
|
||||
background-image: linear-gradient(hsl(50, 100%, 70%), hsl(50, 100%, 50%));
|
||||
}
|
||||
|
||||
span[class~="two"] :nth-child(1),
|
||||
span[class~="two"] :nth-child(2) {
|
||||
background-image: linear-gradient(hsl(50, 100%, 70%), hsl(50, 100%, 50%));
|
||||
}
|
||||
|
||||
span[class~="one"] :first-child {
|
||||
background-image: linear-gradient(hsl(50, 100%, 70%), hsl(50, 100%, 50%));
|
||||
}
|
||||
```
|
Loading…
Reference in New Issue