Nội dung bài viết
Mấy nay lại có hứng khởi viết tiếp bài cho serie hướng dẫn cắt PSD sang HTML toàn tập với mục đích hoàn thành serie trong tháng 12 cuối năm này cũng như giúp các bạn mới hiểu biết được quy trình cắt PSD sang HTML nó như thế nào để từ đó có thể học và cải thiện kiến thức – trình độ hơn.
Ở bài trước chúng ta đã làm Block Feature với layout khá phức tạp tuy nhiên với việc phân tích kỹ càng thì chúng ta đã hoàn thành nó một cách trơn tru. Hôm nay chúng ta sẽ tiếp tục làm các block còn lại của Design đó là Block Tweet và Block Images Shop.
# Block Tweet
Nhìn vào Block Tweet thì hiểu ngay đó là Slider dạng hiển thị các tweet của các tác giả. Tuy nhiên trong phạm vi serie này mình chỉ hướng dẫn cắt PSD sang HTML CSS mà thôi nên mình sẽ code như một block bình thường chứ không dùng Javascripts hay thư viện Slider gì ở đây cả.
Cấu trúc của nó khá đơn giản như Block Feature Header ở phần trước. Có màu nền là màu xám lợt, một icon twitter(dùng font-awesome), một thẻ tiêu đề, một đoạn chữ và cuối cùng là phân trang. Từ đây ta có cấu trúc HTML như sau
<div class="tweet"> <i class="fab fa-twitter tweet__icon"></i> <h2 class="tweet__heading"> <span class="tweet__location">AOD New York</span> <span class="tweet__author">@aod</span> <time class="tweet__time">35min</time> </h2> <p class="tweet__content">Lorem ipsum... </p> <ul class="tweet__pagi"> <li class="tweet__pagi-item is-active"></li> <li class="tweet__pagi-item"></li> <li class="tweet__pagi-item"></li> </ul> </div>
Trong thẻ h2
có class tweet__heading
mình thấy có ba phần nhỏ đó là địa chỉ, tác giả và thời gian cho nên mình chia ra các thẻ span
và time
để code cho chuẩn.
.tweet { padding: 12.5rem 1.5rem 7rem; background-color: var(--bg-light); text-align: center; } .tweet__icon { font-size: 4rem; color: #00aefd; margin-bottom: 3rem; } .tweet__heading { font-size: 1.8rem; margin-bottom: 2rem; } .tweet__location, .tweet__author { color: var(--heading); font-weight: bold; } .tweet__time { color: var(--text); font-weight: 300; padding-left: 1.5rem; margin-left: 1.5rem; position: relative; }
Các bạn để ý ở mũi tên trên hình chỗ thời gian có dấu “/” cách ra một đoạn. Các bạn có thể gõ khoảng trắng rộng ra hoặc có thể dùng :before
hay :after
đều được. Nếu dùng :before
hay :after
thì các bạn có thể code như sau
.tweet__time:before { content: "/"; position: absolute; left: 0; top: 0; }
Đoạn chữ thì không có gì ngoài kiểu chữ in nghiêng và độ rộng tối đa là 104.5rem. Còn ở phân trang tweet__pagi
mình dùng cấu trúc ul li
và trong hình thì mình thấy hình tròn đầu tiên nó có màu nền cho nên mình hiểu là nó active
nên mình thêm một class is-active
vào nó
.tweet__content{ font-size: 2.4rem; line-height: 1.4; color: var(--text); font-style: italic; max-width: 104.5rem; margin: 0 auto 10rem; } .tweet__pagi { display: flex; align-items: center; justify-content: center; } .tweet__pagi-item { width: 1.5rem; height: 1.5rem; border-radius: 5rem; border: 1px solid var(--text); margin-left: .5rem; margin-right: .5rem; } .tweet__pagi-item.is-active { background-color: var(--text); }
Thế là xong block Tweet. Các bạn lưu ý là các thông số mình lấy từ Design ra bằng Photoshop. Cái này các bạn nên tự làm nhá chớ mình không chỉ cách lấy như thế nào nữa nhé. Giờ chúng ta sẽ tiếp tục đến block Images Shop dưới đây.
# Block Images Shop
Cấu trúc của block này khá là phức tạp nhưng nhìn kỹ thì ta có thể chia layout thành ba phần bằng nhau. Phần một và phần ba giống nhau nhưng ngược hướng ta có thể dùng CSS Flexbox với thuộc tính order
hoặc flex-direction: column-reverse
. Còn phần hai thì quá đơn giản chỉ chia thành hai cột bằng nhau mà thôi.
Trong từng phần tử bên trong block Images Shop mình có kẻ đường line màu xanh cho các bạn dễ nhìn thấy có một hình nền và một hình logo kèm chữ “Shop now” khi rê chuột vào nó mới hiện ra kèm theo đó là một lớp nền phủ trên màu xanh lá mờ. Hiệu ứng này chúng ta sẽ làm sau.
Cái quan trọng ở đây chính là cấu trúc HTML sao cho hợp lý có thể tái sử dụng, không dư thừa nhiều. Thì mình sẽ phân tích cho các bạn như sau.
Đầu tiên là cấu trúc HTML chung cho từng phần tử trong block Images Shop mình gọi là images__item
nhé. Trong phần tử này ta có một tấm hình nền mình đặt class là images__item-bg
và một tấm hình logo kèm chữ “Shop now” mình đặt chung trong một div
có class là images__item-brand
Từ đây ta có HTML cho nó như sau
<div class="images__item"> <img src="images/img10.jpg" alt="" class="images__item-bg"> <div class="images__item-brand"> <img src="images/img-bicycle-sm.png" alt=""> <h3 class="images__item-text">Shop now</h3> </div> </div>
Bây giờ đến phân tích cấu trúc HTML tổng thể cho block Images Shop. Như đã nói ở trên chúng ta sẽ chia thành ba phần bằng nhau cho nên ở đây mình sẽ dùng thẻ ul li
để dễ dàng dùng Selectors trong CSS để tùy chỉnh. Mình tạo cấu trúc HTML đơn giản và CSS như sau
<ul class="images"> <li class="images__block"></li> <li class="images__block"></li> <li class="images__block"></li> </ul>
.images { display: flex; justify-content: space-between; height: 53.5rem; overflow: hidden; } .images__block { width: 33.333%; height: 100%; display: flex; }
Mình đo từ Design trong Photoshop thì thấy block Images Shop này có chiều cao tối đa là 535px(53.5rem) cho nên mình set cứng cho nó luôn. Sau đó mình dùng CSS Flexbox để tạo layout và cho các images__block
cao 100% và rộng bằng nhau 33.333%.
Và mình thấy các images__block
đều sẽ dùng CSS Flexbox cho nên mình set thêm thuộc tính display: flex
vào luôn ở trên. Thế là xong cấu trúc tổng thể. Bây giờ chúng ta sẽ đi vào chi tiết từng phần một nhé.
# Block images số 1
Ở đây mình thấy nó chia ra thành hai phần trên và dưới. Phần trên có hai phần tử hình ảnh(images__item
) mỗi cái chiếm một nửa còn phần dưới thì chỉ có một phần tử hình ảnh chiếm full hết. Ta sẽ có HTML như sau
<li class="images__block"> <div class="images__block-item"> <div class="images__item"> <img src="images/img10.jpg" alt="" class="images__item-bg"> <div class="images__item-brand"> <img src="images/img-bicycle-sm.png" alt=""> <h3 class="images__item-text">Shop now</h3> </div> </div> <div class="images__item"> <img src="images/img11.jpg" alt="" class="images__item-bg"> <div class="images__item-brand"> <img src="images/img-bicycle-sm.png" alt=""> <h3 class="images__item-text">Shop now</h3> </div> </div> </div> <div class="images__block-item"> <div class="images__item"> <img src="images/img12.jpg" alt="" class="images__item-bg"> <div class="images__item-brand"> <img src="images/img-bicycle-sm.png" alt=""> <h3 class="images__item-text">Shop now</h3> </div> </div> </div> </li>
Đoạn images__item
mình đã có phân tích trước ở phần cấu trúc HTML chung rồi nhé. Ở block số 1 này mình thấy nó hiển thị dạng cột cho nên mình sẽ dùng CSS Flexbox với flex-direction: column
và tạo hai class images__block-item
để chia ra một phần trên và một phần dưới . Tạm thời mình có CSS như sau
.images__block:first-child { flex-direction: column; } .images__block-item { height: 50%; width: 100%; }
Sau đó mình thấy ở phần trên có hai cột bằng nhau cho nên mình lại dùng CSS Flexbox và đồng thời mình cho toàn bộ các images__item
có chiều cao 100% và độ rộng là 50% luôn. Để chọn phần tử đầu tiên mà không phải dạng danh sách như ul li
hay ol li
mà dạng div div
thì mình dùng :first-of-type
ta có
.images__block-item:first-of-type { display: flex; } .images__item { width: 50%; height: 100%; }
Vì mình set toàn bộ images__item
có độ rộng là 50% nên phần tử hình ảnh ở phần dưới chắc chắn sẽ ảnh hưởng vì dùng chung class cho nên mình phải style cho nó ra 100% và để chọn phần tử thứ hai(trường hợp ở đây là cuối cùng) mà không phải dạng danh sách thì ta dùng :last-of-type
.images__block-item:last-of-type .images__item { width: 100%; }
Như vậy là xong block images số 1. Tiếp theo dưới đây sẽ là block images số 2, các bạn tiếp tục theo dõi nà.
# Block images số 2
Ở block này rất đơn giản chúng ta chỉ cần có hai images__item
mà thôi rồi thay các đường dẫn hình ảnh là xong vì CSS chúng ta làm ở trên đã đảm nhiệm luôn cho block này luôn rồi nên chỉ cần HTML như này là xong.
<li class="images__block"> <div class="images__item"> <img src="images/img13.jpg" alt="" class="images__item-bg"> <div class="images__item-brand"> <img src="images/img-bicycle-sm.png" alt=""> <h3 class="images__item-text">Shop now</h3> </div> </div> <div class="images__item"> <img src="images/img0.jpg" alt="" class="images__item-bg"> <div class="images__item-brand"> <img src="images/img-bicycle-sm.png" alt=""> <h3 class="images__item-text">Shop now</h3> </div> </div> </li>
# Block images số 3
Ở block này tất cả mọi thứ đều như block số 1 ngoại trừ thay đường dẫn các hình ảnh và chỉ chỉnh duy nhất một thuộc tính CSS mà thôi đó chính là flex-direction
với giá trị là column-reverse
để đảo ngược hướng hiển thị. Ta có HTML và CSS như sau
<li class="images__block"> <div class="images__block-item"> <div class="images__item"> <img src="images/img2.jpg" alt="" class="images__item-bg"> <div class="images__item-brand"> <img src="images/img-bicycle-sm.png" alt=""> <h3 class="images__item-text">Shop now</h3> </div> </div> <div class="images__item"> <img src="images/img4.jpg" alt="" class="images__item-bg"> <div class="images__item-brand"> <img src="images/img-bicycle-sm.png" alt=""> <h3 class="images__item-text">Shop now</h3> </div> </div> </div> <div class="images__block-item"> <div class="images__item"> <img src="images/img6.jpg" alt="" class="images__item-bg"> <div class="images__item-brand"> <img src="images/img-bicycle-sm.png" alt=""> <h3 class="images__item-text">Shop now</h3> </div> </div> </div> </li>
.images__block:last-child { flex-direction: column-reverse; }
Thế là xong block images số 3. Các bạn để ý mình có vẽ các mũi tên xanh chỉ chỗ background màu xanh lợt, chữ “Shop now” và logo. Chúng ta sẽ làm hiệu ứng đó tiếp theo dưới đây khi rê chuột vào.
# Images item style
<div class="images__item"> <img src="images/img10.jpg" alt="" class="images__item-bg"> <div class="images__item-brand"> <img src="images/img-bicycle-sm.png" alt=""> <h3 class="images__item-text">Shop now</h3> </div> </div>
Để sử dụng thuộc tính position: absolute
và căn chỉnh cho images__item-brand
nằm trung tâm hay làm background mờ phủ lên cái images__item
bằng :before hay :after thì images__item
phải có thuộc tính position
và giá trị ở đây là relative
. Cấu trúc HTML đã có ở trên, chúng ta sẽ CSS như sau
.images__item { position: relative; } .images__item:before { content: ""; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: #70c6a6c9; z-index: 10; display: none; } .images__item-bg { width: 100%; height: 100%; object-fit: cover; } .images__item-brand { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 20; text-align: center; display: none; } .images__item-brand > img { margin: 0 auto; } .images__item-text { text-transform: uppercase; color: white; font-size: 1.6rem; margin-top: .5rem; font-weight: bold; }
Với đoạn code đầu tiên thì mình dùng :before để làm background phủ lên hết nhưng nó phải nằm dưới images__item-brand
cho nên ở phần CSS cái images__item-brand
mình cho z-index
cao hơn để nó nằm lên trên đồng thời mình set cái background mờ ở :before
và images__item-brand
thuộc tính display: none
để ẩn đi.
Sau đó mình muốn khi rê chuột vào images__item
thì nó mới hiện lên thì chúng ta sẽ CSS đơn giản như này
.images__item:hover:before, .images__item:hover .images__item-brand { display: block; }
Thế là xong hết toàn bộ. Cuối cùng chúng ta sẽ có kết quả như mong muốn. Các bạn có thể nhấn vào đây để xem kết quả online hoặc nhấn vào đây để tải source code về tham khảo đối chiếu nhé.
# Tạm kết
Phù!!! Thế là xong phần 4. Quả là một chặng đường dài trong việc cắt PSD sang HTML CSS này. Còn vài phần nữa thôi là hết rồi. Hi vọng các bạn còn đủ kiên trì theo dõi serie này để ngày càng cải thiện kiến thức và trình độ hơn nà. Chúc các bạn một ngày tốt lành..
Vẫn luôn theo dõi series này của bạn và mong chờ từng ngày, rất hi vọng sau này bạn sẽ viết nhiều bài hướng dẫn bổ ích về Front-end để cho mọi người cùng học hỏi ! Một lần nữa cảm ơn bạn rất nhiều ^^
keke. oke bạn nè. Cám ơn comment chất lừ của bạn nha.
Hóng phần kế tiếp kaka bài viết quá hay
Cám ơn bạn nà
A ơi, trong bài này em thấy dùng first-child cũng được mà anh 🙂 Theo em đọc hiểu về first-child với first-of-type nó chỉ khác nhau là first-child thì chỉ dùng đc khi nó là con đầu tiên của cha nó, còn first-of-type là con đầu tiên mà thuộc loại nó đang dùng thuộc cha nó :D. Trong bài em thấy mấy tên kia đều là con đầu cả, ko cần phải phân biệt type nào. Nếu em sai mong a chỉ lại 😛
Cám ơn em nha. I got it. Do anh quen dùng child cho list không ah, còn không phải list thì a dùng type. Cám ơn em vì kiến thức nà <3
Cho em hỏi cái padding right left a hay để 1.5rem, số đó có ý nghĩa gì không ạ, hay mình thường dùng padding 2 bên 15 pixel a?
A hay dùng 1.5rem cho padding-left và padding-right là bởi vì xuống mobile khi có padding 2 bên cho content không dính lề thôi em, em có thể set bao nhiêu cũng đc miễn sao trông UI đẹp là oke rồi nà. Còn nếu làm dự án team đồ thì nó có yêu cầu thì mình làm theo yêu cầu.
Dạ =D Đó là câu trả lời em mong muốn rồi ạ. Em cảm ơn anh nhiều nhé
a cho e hỏi ở chỗ .tweet__pagi-item {
margin-left: .5rem;
margin right: .5rem;}
dấu chấm ở .5rem đó có nghĩa là sao ạ
À trong CSS 0.5 có thể viết thành .5 nha em. Còn nếu em hỏi về đơn vị “em” hay “rem” thì em vào mục useful đọc bài đơn vị em và rem là hiểu nhé.
em cảm ơn ạ, a cho em hỏi thêm là :
.inp_collapse:checked + .nav_bg{
transform: scale(80);
}
.inp_collapse:checked ~ .nav_list{
transform: translateX(0);
}
dấu + với dấu ~ ở đoạn code trên có nghĩa là gì vậy ạ, e muốn search gg mà không biết từ khóa ạ
Ah nó là Combinator và Sibling đó em. em có thể xem tại đây: https://www.w3schools.com/css/css_combinators.asp
cảm ơn a nhiều ạ, hi vọng a sẽ ra tiếp nhiều series mới ạ
Oke em nè, anh sẽ cố gắng ra nhiều bài viết chi tiết và chất lượng cho cộng đồng.
0.5rem
là 0.5 á
0.5rem đó bạn
Mới học dặt tên class BEM này loạn xì ngậu quá anh, mỗi lần code css lại phải quay lại nhìn cấu trúc html =))
Cố lên nà keke
bài viết hay thật mà thấy anh dàn layout toàn sài flexbox không nhỉ, hóng bài psd2html dàn layout với grid
Grid chia bố cục tổng thể hoy, còn chi tiết thì dùng flexbox tốt hơn
Cảm ơn anh, bài viết rất chi tiết dễ hiểu =)))
hihi cám ơn em nha