4. Dự án Natours: sử dụng CSS nâng cao và Sass (phần 2):
4.1. Convert CSS của phần 1 sang Sass: Variables & Nesting.
Phương pháp tiếp cận:
- Variables: những giá trị được khai báo được sử dụng nhiều lần, lặp đi lặp lại trong toàn bộ dự án, sẽ được xem xét để chuyển thành biến. VD: mã màu nhận diện, font sử dụng, kích thước tiêu chuẩn...
// COLORS
$color-primary: #55c57a;
$color-primary-light: #7ed56f;
$color-primary-dark: #28b485;
$color-secondary-light: #ffb900;
$color-secondary-dark: #ff7730;
$color-tertiary-light: #2998ff;
$color-tertiary-dark: #5643fa;
$color-grey-light-1: #f7f7f7;
$color-grey-light-2: #eee;
$color-grey-dark: #777;
$color-grey-dark-2: #999;
$color-grey-dark-3: #333;
$color-white: #fff;
$color-black: #000;
// FONT
$default-font-size: 1.6rem;
- Nesting: sử dụng tính năng Nesting của sass để refactoring lại code css. Nhờ sử dụng BEM trong việc đặt tên Class ta có thể thức hiện công việc này rất dễ dàng và hợp lý:
// _header.scss
.header {
height: 95vh;
background-image: linear-gradient(
to right bottom,
rgba($color-primary-light, 0.8),
rgba($color-primary-dark, 0.8)),
url(../img/hero.jpg);
background-size: cover;
background-position: top;
position: relative;
-webkit-clip-path: polygon(0 0, 100% 0, 100% 75vh, 0 100%);
clip-path: polygon(0 0, 100% 0, 100% 75vh, 0 100%);
&__logo-box {
position: absolute;
top: 4rem;
left: 4rem;
}
&__logo {
height: 3.5rem;
}
&__text-box {
position: absolute;
top: 40%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
}
}
4.2. Thực hiện cấu trúc CSS với Sass.
Áp dụng cấu trúc 7-1 pattern, ta có thể chia thư mục lưu trữ file scss như sau:
- Chú ý: Thư mục và các file không cần thiết thì không cần tạo vẫn OK.
// main.scss
@import "abstracts/functions";
@import "abstracts/mixins";
@import "abstracts/variables";
@import "base/animations";
@import "base/base";
@import "base/typography";
@import "base/utilities";
@import "components/bg-video";
@import "components/button";
@import "components/card";
@import "components/composition";
@import "components/feature-box";
@import "components/form";
@import "components/popup";
@import "components/story";
@import "layout/footer";
@import "layout/grid";
@import "layout/header";
@import "layout/navigation";
@import "pages/home";
4.3. Các nguyên tắc cơ bản trong Responsive Design & các loại Layout.
Responsive Design:
Fluid Grids & Layouts: cho phép các thành phần nội dung hiển thị một cách phù hợp với mọi kích thước trình duyệt. Nên sử dụng % thay vì px cho các thành phần kích thước liên quan đến bố cục giao diện.
Flexible/Responive Images: tất cả các hình ảnh phải được hiển thị phù hợp trên mọi kích thước của trình duyệt. Cần chú ý giữ nguyên tỉ lệ của ảnh.
Media Query: để thay đổi style của các thành phần giao diện tương ứng vơi các kích thước về chiều rộng (breakpoint). Cho phép chúng ta có thể tạo ra các phiên bản khác nhau của giao diện web tương ứng với kích thước width đcợc cài đặt
Các loại Layouts:
- Float Layouts: là phương pháp xây dưng layouts truyền thống, tương thích với tất các các trình duyệt. Float Layout đơn gian tạo ra 1 nhóm các box đặt cạnh nhau. Các giá trị thường sử dụng là float: left/right/none;
- Flexbox: đây là 1 tính năng hiện đại nhất trong CSS3. Là một kiểu dàn trang (layout mode) mà nó sẽ tự cân đối kích thước của các phần tử bên trong để hiển thị trên mọi thiết bị. Nói theo cách khác, bạn không cần thiết lập kích thước của phần tử, không cần cho nó float, chỉ cần thiết lập nó hiển thị chiều ngang hay chiều dọc, lúc đó các phần tử bên trong có thể hiển thị theo ý muốn. Chi tiết chúng ta sẽ được tìm hiểu kỹ hơn ở các buổi sau.
- CSS Grid: CSS Grid Layout giới thiệu rất nhiều khái niệm mới; Có 17 thuộc tính mới để tìm hiểu, và nhiều điều mới hơn để hiểu. Điều này có thể làm cho việc bắt đầu với Grid Layout của CSS trở nên khó khăn, vì các thuật ngữ mới đề cập đến các thuật ngữ khác và bạn có thể gặp phải một sự nhầm lẫn. Ta sẽ được giới thiệu về kỹ thuật này tại bài cuối chương.
4.4. Xây dựng Grid tùy chỉnh với Floats (ko dung CSS Framework)
Các kiến thức cần chú ý khi xây dựng một hệ thống grid column đơn giản:
- Xây dựng cấu trúc HTML cho một Grid System
- Các Attribute Selectors sẽ làm việc như thế nào.
- Cách sử dụng pseudo-class :not.
- Cách sử dụng hàm calc() trong CSS.
<!--Grid Test-->
<section class="grid-test">
<div class="row">
<div class="col-1-of-2">
Col 1 of 2
</div>
<div class="col-1-of-2">
Col 1 of 2
</div>
</div>
<div class="row">
<div class="col-1-of-3">
Col 1 of 3
</div>
<div class="col-1-of-3">
Col 1 of 3
</div>
<div class="col-1-of-3">
Col 1 of 3
</div>
</div>
<div class="row">
<div class="col-1-of-3">
Col 1 of 3
</div>
<div class="col-2-of-3">
Col 2 of 3
</div>
</div>
...
</section>
/*
_grid.scss
Variables
$grid-width: 114rem;
$gutter-vertical: 8rem;
$gutter-horizontal: 6rem;
*/
.row {
max-width: $grid-width;
background-color: #eee;
margin: 0 auto;
&:not(:last-child) {
margin-bottom: $gutter-vertical;
}
@include clearfix;
[class^="col-"] {
background-color: red;
float: left;
&:not(:last-child) {
margin-right: $gutter-horizontal;
}
}
.col-1-of-2 {
width: calc((100% - #{$gutter-horizontal}) / 2);
}
.col-1-of-3 {
width: calc((100% - 2 * #{$gutter-horizontal}) / 3);
}
.col-2-of-3 {
width: calc((100% - 2 * #{$gutter-horizontal}) * 2 / 3);
}
...
}
4.5. Cắt HTML/CSS cho phần About.
Các bước thức hiện:
- Phân tích cấu trúc và các thành phần giao diện => tạo cấu trúc HTML cở bản
<main>
<section class="section-about">
<h2 class="heading-secondary">
Exciting tours for adventurous people
</h2>
...
</section>
</main>
- Style cho tiêu đề heading-secondary, sử dụng kết hợp background-image linear-gradient và background-clip để tạo hiệu ứng text gradient.
//_typography.scss
.heading-secondary {
font-size: 3.5rem;
text-transform: uppercase;
font-weight: 700;
display: inline-block;
background-image: linear-gradient(to right, $color-promary-light, $color-primary-dark);
-webkit-background-clip: text;
color: transparent;
letter-spacing: .2rem;
-webkit-transition: all .2s;
-moz-transition: all .2s;
-ms-transition: all .2s;
-o-transition: all .2s;
transition: all .2s;
&:hover {
-webkit-transform: skewY(2deg) skewX(15deg) scale(1.1);
-moz-transform: skewY(2deg) skewX(15deg) scale(1.1);
-ms-transform: skewY(2deg) skewX(15deg) scale(1.1);
-o-transform: skewY(2deg) skewX(15deg) scale(1.1);
transform: skewY(2deg) skewX(15deg) scale(1.1);
text-shadow: .5rem 1rem 2rem rgba($color-black, .2);
}
}
- Tạo và sử dụng các utilities class (bootstrap cũng thường có sẵn các class này để sử dụng)
//_utilities.scss
.u-center-text { text-align: center; }
.u-margin-bottom-8 { margin-bottom: 8rem; }
<section class="section-about">
<div class="u-center-text">
<h2 class="heading-secondary">
Exciting tours for adventurous people
</h2>
</div>
...
</section>
- Xây dựng HTML cho phần nội dung bằng cách sử dụng Grid System
<div class="row">
<div class="col-1-of-2">
<h3 class="heading-tertiary u-margin-bottom-small">You're going to fall in love with nature</h3>
<p class="paragraph">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Aperiam, ipsum sapiente aspernatur libero repellat quis consequatur
ducimus quam nisi exercitationem omnis earum qui.
</p>
<h3 class="heading-tertiary u-margin-bottom-small">Live adventures like you never have before</h3>
<p class="paragraph">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Asperiores nulla deserunt voluptatum nam.
</p>
<a href="#" class="btn-text">Learn more →</a>
</div>
<div class="col-1-of-2">
<div class="composition">
<img src="img/nat-1-large.jpg" alt="Photo 1" class="composition__photo composition__photo--p1">
<img src="img/nat-2-large.jpg" alt="Photo 2" class="composition__photo composition__photo--p2">
<img src="img/nat-3-large.jpg" alt="Photo 3" class="composition__photo composition__photo--p3">
</div>
</div>
</div>
- CSS cho phần nội dung text
//_typography.scss
.heading-tertiary {
font-size: 1.6rem;
font-weight: 700;
}
.paragraph {
font-size: 1.6rem;
&:not(:last-child) {
margin: 3rem;
}
}
//_utilities.scss
.u-margin-bottom-small {
margin-bottom: 2rem;
}
.u-margin-bottom-medium {
margin-bottom: 4rem;
}
.u-margin-bottom-big {
margin-bottom: 8rem;
}
//_button.scss
.btn-text {
&:link,
&::visited {
color: $color-primary;
display: inline-block;
text-decoration: none;
border-bottom: 1px solid $color-primary;
}
&:hover {
background-color: $color-primary;
color: $color-white;
box-shadow: 0 1rem 2rem rgba($color-black, .15);
transform: translateY(-2px);
}
&:active {
box-shadow: 0 0.5rem 1rem rgba($color-black, .15);
transform: translateY(0);
}
}
- CSS cho phần hình ảnh, khởi tạo như một component
//_composition.scss
.compisition {
position: relative;
&__photo {
width: 55%;
box-shadow: 0 1.5rem 4rem rgba($color-black, .4);
border-radius: 2px;
position: absolute;
z-index: 10;
transition: all .2s;
outline-offset: 2rem;
&--p1 {
left: 0;
top: -2rem;
}
&--p2 {
right: 0;
top: 2rem;
}
&--p3 {
left: 20%;
top: 10rem;
}
&--p4 {
left: 0;
top: -2rem;
}
&:hover {
outline: 1.5rem solid $color-primary;
transform: scale(1.05);
box-shadow: 0 2.5rem 4rem rgba($color-black, .5);
z-index: 20;
}
}
&:hover &__photo::not(:hover) {
transform: scale(0.95);
}
}
4.6. Cắt HTML/CSS cho phần Features.
Những kỹ năng cần chú ý để thực hiện phần giao diện trên:
- Sự dụng Icon Font trong dự án như thế nào? (icon font dùng trong project: http://linea.io/)
- Làm quen với CSS Skew
- Tương tác với Child Selector
<section class="section-features">
<div class="row">
<div class="col-1-of-4">
<div class="feature-box">
<i class="feature-box__icon icon-basic-world"></i>
<h3 class="heading-tertiary u-margin-bottom-small">Explore the world</h3>
<p class="feature-box__text">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Aperiam, ipsum sapiente aspernatur.
</p>
</div>
</div>
<div class="col-1-of-4">
<div class="feature-box">
<i class="feature-box__icon icon-basic-compass"></i>
<h3 class="heading-tertiary u-margin-bottom-small">Meet nature</h3>
<p class="feature-box__text">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Aperiam, ipsum sapiente aspernatur.
</p>
</div>
</div>
.....
</div>
</section>
// Component
//_featured-box.scss
.feature-box {
background-color: rgba($color-white, .8);
font-size: 1.5rem;
padding: 2.5rem;
text-align: center;
border-radius: 3px;
box-shadow: 0 1.5rem 4rem rgba($color-black, .15);
transition: transform .3s;
&__icon {
font-size: 6rem;
margin-bottom: .5rem;
display: inline-block;
background-image: linear-gradient(to right, $color-primary-light, $color-primary-dark);
-webkit-background-clip: text;
color: transparent;
}
&:hover {
transform: translateY(-1.5rem) scale(1.03);
}
}
//_home.scss
.section-features {
padding: 20rem 0;
background-image: linear-gradient(
to right bottom,
rgba($color-primary-light, 0.8),
rgba($color-primary-dark, 0.8)),
url(../img/nat-4.jpg);
background-size: cover;
transform: skewY(-7deg); // Skew container block
margin-top: -10rem;
& > * {
transform: skewY(7deg); // skew all child element
}
}
4.7. Cắt HTML/CSS cho phần Tours.
Những kỹ năng cần chú ý:
- Cách để tạo hiệu ứng Rotate Card.
- Sử dụng perpective trong CSS như thế nào?
- Tác dụng của thuộc tính CSS : backface-visibility
- Cách sử dụng chế độ background blend trong CSS
- Khi nào thì dùng tới box-decoration-break
<section class="section-tours" id="section-tours">
<div class="u-center-text u-margin-bottom-big">
<h2 class="heading-secondary">
Most popular tours
</h2>
</div>
<div class="row">
<div class="col-1-of-3">
<div class="card">
<div class="card__side card__side--front">
<div class="card__picture card__picture--1">
</div>
<h4 class="card__heading">
<span class="card__heading-span card__heading-span--1">The Sea Explorer</span>
</h4>
<div class="card__details">
<ul>
<li>3 day tours</li>
<li>Up to 30 people</li>
<li>2 tour guides</li>
<li>Sleep in cozy hotels</li>
<li>Difficulty: easy</li>
</ul>
</div>
</div>
<div class="card__side card__side--back card__side--back-1">
<div class="card__cta">
<div class="card__price-box">
<p class="card__price-only">Only</p>
<p class="card__price-value">$297</p>
</div>
<a href="#popup" class="btn btn--white">Book now!</a>
</div>
</div>
</div>
</div>
......
</div> <!-- END ROW -->
</section> <!-- END SECTION -->
Rotate Card - Khung cơ bản
// _card.scss
.card {
// FUNCTIONALITY
// Thông thường khi nhìn một thành phần ta sẽ thấy thành phần có dạng 2D (chiều rộng và chiều cao),
// để nhìn vật thể 3D ta cần có thêm chiều sâu, thuộc tính perspective sẽ cho ta thấy được chiều sâu
// của thành phần, khoảng chiều sâu được tính từ điểm đặt ban đầu tới giá trị của perspective
// (theo đơn vị pixel).
perspective: 150rem;
-moz-perspective: 150rem;
position: relative;
height: 52rem;
&__side {
height: 52rem;
transition: all .8s ease;
position: absolute;
top: 0;
left: 0;
width: 100%;
backface-visibility: hidden; // Xác định bề mặt sau của thành phần khi thực hiện một chuyển động xoay.
border-radius: 3px;
overflow: hidden;
box-shadow: 0 1.5rem 4rem rgba($color-black, .15);
&--front {
background-color: $color-white;
}
&--back {
transform: rotateY(180deg);
&-1 {
background-image: linear-gradient(to right bottom, $color-secondary-light, $color-secondary-dark);
}
&-2 {
background-image: linear-gradient(to right bottom, $color-primary-light, $color-primary-dark);
}
&-3 {
background-image: linear-gradient(to right bottom, $color-tertiary-light, $color-tertiary-dark);
}
}
}
&:hover &__side--front {
transform: rotateY(-180deg);
}
&:hover &__side--back {
transform: rotateY(0);
}
}
Rotate Cart - Mặt trước
.card {
// FRONT SIDE STYLING
&__picture {
background-size: cover;
height: 23rem;
// Thuộc tính backkground-blend-mode có tác dụng thiết lập chế độ hòa trộn của từng lớp layer.
background-blend-mode: screen;
-webkit-clip-path: polygon(0 0, 100% 0, 100% 85%, 0 100%);
clip-path: polygon(0 0, 100% 0, 100% 85%, 0 100%);
border-top-left-radius: 3px;
border-top-right-radius: 3px;
&--1 {
background-image: linear-gradient(to right bottom, $color-secondary-light, $color-secondary-dark),
url(../img/nat-5.jpg);
}
&--2 {
background-image: linear-gradient(to right bottom, $color-primary-light, $color-primary-dark),
url(../img/nat-6.jpg);
}
&--3 {
background-image: linear-gradient(to right bottom, $color-tertiary-light, $color-tertiary-dark),
url(../img/nat-7.jpg);
}
}
&__heading {
font-size: 2.8rem;
font-weight: 300;
text-transform: uppercase;
text-align: right;
color: $color-white;
position: absolute;
top: 12rem;
right: 2rem;
width: 75%;
}
&__heading-span {
padding: 1rem 1.5rem;
// Thiết lập dáng vẻ của hình nền và đường viền của một phần tử tại trang ngắt,
// hoặc đối với phần tử in-line , tại dòng ngắt
-webkit-box-decoration-break: clone;
box-decoration-break: clone;
&--1 {
background-image: linear-gradient(to right bottom,
rgba($color-secondary-light, .85),
rgba($color-secondary-dark, .85));
}
&--2 {
background-image: linear-gradient(to right bottom,
rgba($color-primary-light, .85),
rgba($color-primary-dark, .85));
}
&--3 {
background-image: linear-gradient(to right bottom,
rgba($color-tertiary-light, .85),
rgba($color-tertiary-dark, .85));
}
}
&__details {
padding: 3rem;
ul {
list-style: none;
width: 80%;
margin: 0 auto;
li {
text-align: center;
font-size: 1.5rem;
padding: 1rem;
&:not(:last-child) {
border-bottom: 1px solid $color-grey-light-2;
}
}
}
}
}
Rotate Card - Mặt sau:
.card {
// Back SIDE STYLING
&__cta {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 90%;
text-align: center;
}
&__price-box {
text-align: center;
color: $color-white;
margin-bottom: 8rem;
}
&__price-only {
font-size: 1.4rem;
text-transform: uppercase;
}
&__price-value {
font-size: 6rem;
font-weight: 100;
}
}
4.8. Cắt HTML/CSS cho phần Stories.
Các kỹ năng cần chú ý:
- Kỹ thuật tạo hiệu ứng text bao quanh 1 hình ảnh, sử dụng shape-outsite và float
- Áp dụng filter trong xử lý ảnh.
- Cách tạo Video background.
- Khi nào thì cần tới thuộc tính CSS: object fit
<section class="section-stories">
<div class="u-center-text u-margin-bottom-big">
<h2 class="heading-secondary">
We make people genuinely happy
</h2>
</div>
<div class="row">
<div class="story">
<figure class="story__shape">
<img src="img/nat-8.jpg" alt="Person on a tour" class="story__img">
<figcaption class="story__caption">Mary Smith</figcaption>
</figure>
<div class="story__text">
<h3 class="heading-tertiary u-margin-bottom-small">I had the best week ever with my family</h3>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Aperiam, ipsum sapiente aspernatur libero repellat quis consequatur
ducimus quam nisi exercitationem omnis earum qui. Aperiam, ipsum sapiente aspernatur libero
repellat quis consequatur ducimus quam nisi exercitationem omnis earum qui.
</p>
</div>
</div>
</div>
...
<div class="u-center-text u-margin-top-huge">
<a href="#" class="btn-text">Read all stories →</a>
</div>
</section>
Story components
.story {
width: 75%;
margin: 0 auto;
box-shadow: 0 3rem 6rem rgba($color-black, .1);
background-color: rgba($color-white, .6);
border-radius: 3px;
padding: 6rem;
padding-left: 9rem;
font-size: $default-font-size;
transform: skewX(-12deg);
&__shape {
width: 15rem;
height: 15rem;
float: left;
// Để cho đoạn văn bám vào đường cong của shape, chúng ta cần thay đổi nó thành một shape
// thật sự thông qua thuộc tính shape-outside; trong trường hợp này, chúng ta sẽ thêm một
// hàm circle() truyền vào như là một giá trị
-webkit-shape-outside: circle(50% at 50% 50%);
shape-outside: circle(50% at 50% 50%);
-webkit-clip-path: circle(50% at 50% 50%);
clip-path: circle(50% at 50% 50%);
transform: translateX(-3rem) skewX(12deg);
position: relative;
}
&__img {
height: 100%;
transform: translateX(-4rem) scale(1.4);
backface-visibility: hidden;
transition: all .5s;
}
&__text {
transform: skewX(12deg);
}
&__caption {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, 20%);
color: $color-white;
text-transform: uppercase;
font-size: 1.7rem;
text-align: center;
opacity: 0;
transition: all .5s;
backface-visibility: hidden;
}
&:hover &__caption {
opacity: 1;
transform: translate(-50%, -50%);
}
&:hover &__img {
transform: translateX(-4rem) scale(1);
// làm nhòe ảnh
filter: blur(3px) brightness(80%);
}
}
Video Background
// Story Section
<div class="bg-video">
<video class="bg-video__content" autoplay muted loop>
<source src="img/video.mp4" type="video/mp4">
<source src="img/video.webm" type="video/webm">
Your browser is not supported!
</video>
</div>
// _bg-video.scss
.bg-video {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
z-index: -1;
opacity: .15;
overflow: hidden;
&__content {
height: 100%;
width: 100%;
// Fit video content with parent block, like background-size: cover;
object-fit: cover;
}
}
4.9. Cắt HTML/CSS cho phần Booking.
Những kỹ năng cần chú ý:
- Tạo dải màu nền với solid color gradients
- Cách để trỏ tới thành phần nằm kế bên một thành phần khác trong CSS.
- Hiểu biết về pseudo:element - ::input=placeholder
- Khi nào thì sử dung các pseudo:class :focus; :invalid; placeholder-show; :checked
- Style cho radio button trong CSS
<section class="section-book">
<div class="row">
<div class="book">
<div class="book__form">
<form action="#" class="form">
<div class="u-margin-bottom-medium">
<h2 class="heading-secondary">
Start booking now
</h2>
</div>
<div class="form__group">
<input type="text" class="form__input" placeholder="Full name" id="name" required>
<label for="name" class="form__label">Full name</label>
</div>
<div class="form__group">
<input type="email" class="form__input" placeholder="Email address" id="email" required>
<label for="email" class="form__label">Email address</label>
</div>
<div class="form__group u-margin-bottom-medium">
<div class="form__radio-group">
<input type="radio" class="form__radio-input" id="small" name="size">
<label for="small" class="form__radio-label">
<span class="form__radio-button"></span>
Small tour group
</label>
</div>
<div class="form__radio-group">
<input type="radio" class="form__radio-input" id="large" name="size">
<label for="large" class="form__radio-label">
<span class="form__radio-button"></span>
Large tour group
</label>
</div>
</div>
<div class="form__group">
<button class="btn btn--green">Next step →</button>
</div>
</form>
</div>
</div>
</div>
</section>
// _home.scss
.section-book {
padding: 15rem 0;
// Tạo màu nền dạng Gradient
background-image: linear-gradient(to right bottom, $color-primary-light, $color-primary-dark);
}
.book {
// Tạo nền với 1 phần màu trắng trong + 1 phần trong suốt
background-image: linear-gradient(105deg,
rgba($color-white, .9) 0%,
rgba($color-white, .9) 50%,
transparent 50%) ,
url(../img/nat-10.jpg);
background-size: 100%;
border-radius: 3px;
box-shadow: 0 1.5rem 4rem rgba($color-black, .2);
height: 50rem;
&__form {
width: 50%;
padding: 6rem;
}
}
Các thành phần trong Form
.form {
// Select tới các thành phần form__group không phải là cuối cùng.
&__group:not(:last-child) {
margin-bottom: 2rem;
}
&__input {
font-size: 1.5rem;
// Inherit -> thừa hưởng giá trị thuộc tính từ thành phần cha.
font-family: inherit;
color: inherit;
padding: 1.5rem 2rem;
border-radius: 2px;
background-color: rbga($color-white, .5);
border: none;
border-bottom: 3px solid transparent;
width: 90%;
display: block;
transition: all .3s;
// :focus trạng thái khi element được lựa chọn.
&:focus {
outline: none;
box-shadow: 0 1rem 2rem rgba($color-black, .1);
border-bottom: 3px solid $color-primary;
}
// :invalid -> thành phần được chọn đang ở trang thái sai nhập liệu
&:focus:invalid {
border-bottom: 3px solid $color-secondary-dark;
}
// Select tới thành phần là text nằm trong ô nhập liệu.
&::-webkit-input-placeholder {
color: $color-grey-dark-2;
}
}
&__label {
font-size: 1.2rem;
font-weight: 700;
margin-left: 2rem;
margin-top: .7rem;
display: block;
transition: all .3s;
}
// eleA + p -> select tới 01 element p nằm kế tiếp eleA
&__input:placeholder-shown + &__label {
opacity: 0;
visibility: hidden;
transform: translateY(-4rem);
}
&__radio-group {
width: 49%;
display: inline-block;
}
// Custom radio button
&__radio-input {
display: none;
}
&__radio-label {
font-size: $default-font-size;
cursor: pointer;
position: relative;
padding-left: 4.5rem;
}
&__radio-button {
height: 3rem;
width: 3rem;
border: 5px solid $color-primary;
border-radius: 50%;
display: inline-block;
position: absolute;
left: 0;
top: -.4rem;
&::after {
content: "";
display: block;
height: 1.3rem;
width: 1.3rem;
border-radius: 50%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: $color-primary;
opacity: 0;
transition: opacity .2s;
}
}
// eleA ~ p -> select tới all element p nằm kế tiếp eleA
&__radio-input:checked ~ &__radio-label &__radio-button::after {
opacity: 1;
}
}
4.10. Cắt HTML/CSS cho phần Footer.
<footer class="footer">
<div class="footer__logo-box">
<img src="img/logo-green-2x.png" alt="Full logo" class="footer__logo">
</div>
<div class="row">
<div class="col-1-of-2">
<div class="footer__navigation">
<ul class="footer__list">
<li class="footer__item"><a href="#" class="footer__link">Company</a></li>
<li class="footer__item"><a href="#" class="footer__link">Contact us</a></li>
<li class="footer__item"><a href="#" class="footer__link">Carrers</a></li>
<li class="footer__item"><a href="#" class="footer__link">Privacy policy</a></li>
<li class="footer__item"><a href="#" class="footer__link">Terms</a></li>
</ul>
</div>
</div>
<div class="col-1-of-2">
<p class="footer__copyright">
Built by <a href="#" class="footer__link">Jonas Schmedtmann</a> for his online course <a href="#" class="footer__link">Advanced CSS and Sass</a>.
Copyright © by Jonas Schmedtmann. You are 100% allowed to use this webpage for both personal
and commercial use, but NOT to claim it as your own design. A credit to the original author, Jonas
Schmedtmann, is of course highly appreciated!
</p>
</div>
</div>
</footer>
.footer {
background-color: $color-grey-dark-3;
padding: 10rem 0;
font-size: 1.4rem;
color: $color-grey-light-1;
&__logo-box {
text-align: center;
margin-bottom: 8rem;
}
&__logo {
width: 15rem;
height: auto;
}
&__navigation {
border-top: 1px solid $color-grey-dark;
padding-top: 2rem;
display: inline-block;
}
&__list {
list-style: none;
}
&__item {
display: inline-block;
&:not(:last-child) {
margin-right: 1.5rem;
}
}
&__link {
&:link,
&:visited {
color: $color-grey-light-1;
background-color: $color-grey-dark-3;
text-decoration: none;
text-transform: uppercase;
display: inline-block;
transition: all .2s;
}
&:hover,
&:active {
color: $color-primary;
box-shadow: 0 1rem 2rem rgba($color-black, .4);
transform: rotate(5deg) scale(1.3);
}
}
&__copyright {
border-top: 1px solid $color-grey-dark;
padding-top: 2rem;
width: 80%;
float: right;
}
}
4.11. Cắt HTML/CSS cho phần Navigation.
Những kỹ năng cần chú ý:
- "Checkbox Hack" là gì và cách sử dụng nó trong CSS
- Tạo Animation Functions với việc sử dụng cubic bezier curves
- Tạo sự chuyển động với "solid color gradients"
- Cách sử dụng transform-origin
<div class="navigation">
<input type="checkbox" class="navigation__checkbox" id="navi-toggle">
<label for="navi-toggle" class="navigation__button">
<span class="navigation__icon"> </span>
</label>
<div class="navigation__background"> </div>
<nav class="navigation__nav">
<ul class="navigation__list">
<li class="navigation__item"><a href="#" class="navigation__link"><span>01</span>About Natous</a></li>
<li class="navigation__item"><a href="#" class="navigation__link"><span>02</span>Your benfits</a></li>
<li class="navigation__item"><a href="#" class="navigation__link"><span>03</span>Popular tours</a></li>
<li class="navigation__item"><a href="#" class="navigation__link"><span>04</span>Stories</a></li>
<li class="navigation__item"><a href="#" class="navigation__link"><span>05</span>Book now</a></li>
</ul>
</nav>
</div>
CSS button menu
// _navigation.scss
.navigation {
// hide checkbox
&__checkbox {
display: none;
}
// show label of checkbox likes a menu button
&__button {
background-color: $color-white;
height: 7rem;
width: 7rem;
position: fixed;
top: 6rem;
right: 6rem;
border-radius: 50%;
z-index: 2000;
box-shadow: 0 1rem 3rem rgba($color-black, .1);
text-align: center;
cursor: pointer;
}
...
CSS cho Menu background lúc chưa được click
&__background {
height: 6rem;
width: 6rem;
border-radius: 50%;
position: fixed;
top: 6.5rem;
right: 6.5rem;
// radial-gradient -> tạo gradient hướng tâm
background-image: radial-gradient($color-primary-light, $color-primary-dark);
z-index: 1000;
transition: transform .8s cubic-bezier(0.86, 0, 0.07, 1);
//transform: scale(80);
}
CSS cho các thành phần trong menu
&__nav {
height: 100vh;
position: fixed;
top: 0;
left: 0;
z-index: 1500;
opacity: 0;
width: 0;
transition: all .8s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
&__list {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
list-style: none;
text-align: center;
width: 100%;
}
&__item {
margin: 1rem;
}
&__link {
&:link,
&:visited {
display: inline-block;
font-size: 3rem;
font-weight: 300;
padding: 1rem 2rem;
color: $color-white;
text-decoration: none;
text-transform: uppercase;
background-image: linear-gradient(120deg, transparent 0%, transparent 50%, $color-white 50%);
background-size: 220%;
transition: all .4s;
span {
margin-right: 1.5rem;
display: inline-block;
}
}
&:hover,
&:active {
background-position: 100%;
color: $color-primary;
transform: translateX(1rem);
}
}
4.12. Làm Popup bằng Pure CSS.
Các kỹ năng cần chú ý:
- Phương pháp tạo Popup chỉ bằng CSS.
- Pseudo-class :target được sử dụng như thế nào.
- Tạo các box có độ cao bằng nhau với display: table-cell
- Tạo text column bằng CSS
- Tự động tạo dấu nối (-) trong text với CSS hyphens
<div class="popup" id="popup">
<div class="popup__content">
<div class="popup__left">
<img src="img/nat-8.jpg" alt="Tour photo" class="popup__img">
<img src="img/nat-9.jpg" alt="Tour photo" class="popup__img">
</div>
<div class="popup__right">
<a href="#section-tours" class="popup__close">×</a>
<h2 class="heading-secondary u-margin-bottom-small">Start booking now</h2>
<h3 class="heading-tertiary u-margin-bottom-small">Important – Please read these terms before booking</h3>
<p class="popup__text">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Sed sed risus pretium quam. Aliquam sem et tortor consequat id. Volutpat odio facilisis mauris sit
amet massa vitae. Mi bibendum neque egestas congue. Placerat orci nulla pellentesque dignissim enim
sit. Vitae semper quis lectus nulla at volutpat diam ut venenatis. Malesuada pellentesque elit eget
gravida cum sociis natoque penatibus et. Proin fermentum leo vel orci porta non pulvinar neque laoreet.
Gravida neque convallis a cras semper. Molestie at elementum eu facilisis sed odio morbi quis. Faucibus
vitae aliquet nec ullamcorper sit amet risus nullam eget. Nam libero justo laoreet sit. Amet massa
vitae tortor condimentum lacinia quis vel eros donec. Sit amet facilisis magna etiam. Imperdiet sed
euismod nisi porta.
</p>
<a href="#" class="btn btn--green">Book now</a>
</div>
</div>
</div>
.popup {
// Popup container with 100% width & height
height: 100vh;
width: 100%;
position: fixed;
top: 0;
left: 0;
background-color: rgba($color-black, .8);
z-index: 9999;
// hide popup before link active
opacity: 0;
visibility: hidden;
transition: all .3s;
&__content {
// pupup box content
@include absCenter;
width: 75%;
background-color: $color-white;
box-shadow: 0 2rem 4rem rgba($color-black, .2);
border-radius: 3px;
display: table;
overflow: hidden;
opacity: 0;
transform: translate(-50%, -50%) scale(.25);
transition: all .5s .2s;
}
&__left {
// left column
width: 33.333333%;
display: table-cell;
}
&__right {
// right column
width: 66.6666667%;
display: table-cell;
vertical-align: middle;
padding: 3rem 5rem;
}
&__img {
display: block;
width: 100%;
}
&__text {
font-size: 1.4rem;
margin-bottom: 4rem;
-moz-column-count: 2;
-moz-column-gap: 4rem; //1em = 14px;
-moz-column-rule: 1px solid $color-grey-light-2;
column-count: 2;
column-gap: 4rem; //1em = 14px;
column-rule: 1px solid $color-grey-light-2;
-moz-hyphens: auto;
-ms-hyphens: auto;
-webkit-hyphens: auto;
hyphens: auto;
}
//Open states when link is targeted
&:target {
opacity: 1;
visibility: visible;
}
&:target &__content {
opacity: 1;
transform: translate(-50%, -50%) scale(1);
}
&__close {
&:link,
&:visited {
color: $color-grey-dark;
position: absolute;
top: 2.5rem;
right: 2.5rem;
font-size: 3rem;
text-decoration: none;
display: inline-block;
transition: all .2s;
line-height: 1;
}
&:hover {
color: $color-primary;
}
}
}