Hướng dẫn cắt PSD sang HTML toàn tập phần 3

chuyển psd sang html

Sau phần 2 của series hướng dẫn cắt PSD sang HTML toàn tập mình đã drop một thời gian vì bận viết các bài khác cho nên hôm nay quay lại để tiếp tục cho các bạn với mục tiêu trong tháng 12 là xong series hướng dẫn cắt PSD sang HTML toàn tập cho các bạn luôn.

Hôm nay sẽ là phần 3 tiếp nối phần trước thì chúng ta đã làm xong phần Header và Menu. Tiếp đến bây giờ là phần Feature với cấu trúc layout khá phức tạp hơn một chút. Các bạn có thể xem qua hình trước

Wow. Cái trên thì chắc dễ rồi còn ba phần dưới thì sao nhỉ, tạo cấu trúc HTML sao cho đúng, đặt tên class sao cho chuẩn BEM, dễ dàng tái sử dụng, tối ưu code và quan trọng là hiển thị đúng so với Design. Thôi thì không dài dòng nữa chúng ta cùng phân tích và làm nhé.

Nếu bạn nào chưa đọc các bài trước thì nhớ nhấn vào đây để đọc lại nhé vì mình có để liên kết tới source code để các bạn có thể tải về làm theo đó. Nếu các bạn không đọc hai phần trước mà bay vô phần này luôn là tẩu hỏa nhập ma đấy.

# Feature header

Rồi nhìn vào phần Feature Header thì ta thấy có ba phần chính đó là một tấm hình nhỏ, một tiêu đềmột đoạn text đơn giản.

Sau khi dùng các công cụ của Photoshop để lấy các thông số như kích thước chữ, khoảng cách… thì mình đã có các số liệu cụ thể như kích thước của tiêu đề và chữ đều là 18px, màu nền của block là màu xám mờ, khoảng cách giữa các phần tử là 30px, 20px

Lưu ý những thông số như khoảng cách, kích cỡ chữ, màu sắc các bạn nên tự làm bằng cách dùng công cụ có sẵn của Photoshop nhé. Trong phạm vi bài này mình chỉ nói sơ thôi vì chỉ cụ thể từng bước rất là dài và tốn thời gian.

Giờ mình tạo cấu trúc HTML đơn giản cho nó như sau:

<div class="feature">
 <div class="feature__header">
  <img src="images/icon1.png" alt="" class="feature__img-top">
  <h2 class="feature__heading">A NEW GENERATION OF VINTAGE BIKE</h2>
  <p class="feature__header-desc">Lorem Ipsum simply dummy text... </p>
 </div>
</div>

Mình đặt tên theo chuẩn BEM đã nói ở các bài trước để cho code nó clean hơn. Nhiều bạn sẽ hỏi tại sao không đặt thẻ img class là feature__header-img hay là thẻ h2 class là feature__header-title.

Là vì tẹo nữa chúng ta sẽ dùng lại chúng ở phần Feature List bên dưới cho nên mình đặt tên theo kiểu đó để có thể dùng lại sau này.

Lưu ý việc đặt tên: Các bạn có thể đặt tên theo kiểu của mình để học hỏi hoặc có thể đặt theo phong cách riêng của các bạn nhé. Miễn sao các bạn thấy ổn là được rồi nhé.

Cuối cùng dựa vào các thông số mình đã lấy được từ Photoshop chúng ta có CSS như sau

.feature__header {
 padding: 11rem 1.5rem 14rem;
 background-color: var(--bg-light);
}
.feature__img-top {
 margin: 0 auto 3rem;
}
.feature__heading {
 font-size: 1.8rem;
 line-height: 1.6;
 text-transform: uppercase;
 color: var(--heading);
 margin-bottom: 2rem;
 font-weight: bold;
 text-align: center;
}
.feature__header-desc {
 font-size: 1.8rem;
 line-height: 1.6;
 color: var(--text);
 max-width: 106rem;
 margin: 0 auto;
 text-align: center;
}

Chúng ta sẽ có kết quả như mong đợi. Như vậy là xong phần Feature Header nha các bạn. Tiếp đến sẽ là phần khó nhằn bên dưới.

# Feature list

Nhìn vào hình thì tổng thể ta có block1block2 giống nhau nhưng ngược hướng ta có thể dùng CSS flexbox kèm thuộc tính order để thay đổi vị trí vì thế chỉ cần code cái block1 thì cái block2 cũng coi như xong. Cấu trúc block1 và block2 có tỉ lệ là 2/3 và 1/3 tương ứng với 67% và 33%.

Còn block3 thì cấu trúc nó có tới ba cột. Nhìn có cảm giác bằng nhau nhưng sau khi đo thì mình thấy cột thứ hai to hơn hai cột còn lại một chút xíu thôi cho nên nếu chia % ra thì ta sẽ có ba cột tương ứng là cột1(33%) cột 2(34%) và cột 3(33%) hen.

Bây giờ là đến phần code HTML cho chúng. Mình dùng lại HTML ở phần Feature Header ở trên luôn cho các bạn dễ hình dung và thêm thẻ ul để làm cái Feature List này.

<div class="feature">
 <div class="feature__header">
  ...
 </div>
 <ul class="feature__list">
   ...
 </ul>
</div>

Mình dùng ul li để làm dạng danh sách vì mình có tới ba phần tử cũng như có thể chọn các phần tử dễ dàng bằng cách dùng:nth-child, :last-child…. để tiện cho việc tùy chỉnh code một cách trơn tru.

# Feature item

Lưu ý những đoạn code dưới đây nằm trong thẻ ul có class feature__list ở đoạn code trên nhé.

Dựa vào hình ta thấy block1 có hai phần đó là phần hình ảnh bên trái và phần nội dung bên phải và chiều cao của block1 là 600px. Tỉ lệ đã nói ở trên là 67% và 33% và chúng ta sẽ sử dụng CSS Flexbox để làm layout từ đó ta có HTML CSS như thế này.

<li class="feature__item">
 <div class="feature__item-img">
 </div>
 <div class="feature__item-content">
 </div>
</li>
.feature__item {
  display: flex;
  flex-flow: row wrap;
  height: 60rem;
  overflow: hidden;
}
.feature__item-img {
  width: 67%;
  position: relative;/*Note*/
}
.feature__item-content {
  width: 33%;
}

Vì mới chia cột thôi nên lúc này nhìn vào Web vẫn chưa có gì đâu. Nên chúng ta sẽ tiếp tục đưa tiếp các phần tử khác vào.

Ở cột bên trái ta có hai tấm hình, một hình to phủ hết cột và một hình nhỏ nằm chính giữa cột. Ta sẽ có HTML như thế này

<div class="feature__item-img">
  <img src="images/img14.jpg" alt="" class="img__bg">
  <img src="images/img-bicycle-company.png" alt="" class="img__brand">
</div>

Tấm hình phủ hết cột mình cho class là img__bg nghĩa là hình nền và tấm hình nhỏ là img__brand kiểu như là thương hiệu-logo nha. Sau đó mình sẽ CSS cho chúng như dưới đây.

.feature__item .img__bg {
  height: 100%;
  width: 100%;
  object-fit: cover;
}
.feature__item .img__brand {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 2;
}

Các bạn để ý ở class feature__item-img (có Note ở trên) mình cho thuộc tính position: relative để tấm hình có class img__brand có thể dùng position: absolute để căn chỉnh theo đấy. Nếu bạn chưa rõ về thuộc tính position trong CSS thì có thể tìm hiểu tại đây.

Đến cột phải thì trong cột phải thì lại có hai nội dung khác theo chiều dọc. Một trên một dưới nên mình sẽ đặt là content-top và content-bottom.

<div class="feature__item-content">
 <div class="feature__item-content-top">
  <img src="images/icon2.png" alt="" class="feature__img-top">
  <h3 class="feature__heading">Vintage Oliva</h3>
  <p class="feature__item-text">Lorem Ipsum simply...</p>
 </div>
 <div class="feature__item-content-bottom">
  <img src="images/img1.jpg" alt="" class="img__bg">
 </div>
</div>

Phần content-top có chiều cao là 400px và content bottom có chiều cao là 200px. Từ đó mình tính ra được phần trăm chiều cao tương ứng là 67% và 33%.

Các bạn để ý sẽ thấy lại class feature__heading và feature__img-top mình đã dùng trước đó vì như mình đã nói từ trước là các phần sau cũng sử dụng chúng nên phải đặt tên như thế để tái sử dụng.

Nhìn vào cấu trúc cột bên phải mình thấy nội dung theo hướng chiều dọc cho nên mình sẽ dùng flexbox với thuộc tính flex-direction: column. Nếu chưa hiểu kỹ về flexbox thì đừng quên tham khảo lại nó tại đây.

.feature__item-content {
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}

Tiếp đến là content-top ta có padding hai bên là 70px và nội dung của nó nằm giữa cho nên mình cũng dùng flexbox cho nó luôn, kèm theo đó là CSS cho chiều cao của nó(content-top) và cho đoạn chữ(feature__item-text) cũng như chiều cao của content-bottom.

.feature__item-content-top {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 0 7rem;
  position: relative;
  background-color: white;
  height: 67%;
}
.feature__item-text {
  font-size: 1.6rem;
  line-height: 1.6;
  color: var(--text);
  text-align: justify;
  position: relative;
  padding-bottom: 3rem;
}
.feature__item-content-bottom {
  height: 33%;
}

Đến block2 thì cấu trúc HTML y chang block1 luôn chỉ thay đổi đường dẫn hình ảnh và nội dung chữ đồ thôi, rồi sau đó ta dùng thuộc tính order cho class feature__item số hai bằng cách dùng :nth-child như sau thì lúc này phần nội dung hình bên trái sẽ nằm qua bên phải và ngược lại phần nội dung bên phải sẽ chạy qua trái.

.feature__item:nth-child(2) .feature__item-img{
  order: 2;
}

Cuối cùng là block3 với cấu trúc HTML cũng y hệt block1 nhưng thêm một cột hình ảnh mà thôi(feature__item-img). Mình có Note ở cấu trúc HTML dưới đây

<li class="feature__item">
  <div class="feature__item-img">
    <img src="images/img7.jpg" alt="" class="img__bg">
    <img src="images/img-bicycle-text.png" alt="" class="img__brand">
  </div>
  <div class="feature__item-content">
    <div class="feature__item-content-top">
      <img src="images/icon4.png" alt="" class="feature__img-top">
      <h3 class="feature__heading">Retrò Bike - M. Hulot</h3>
      <p class="feature__item-text">Lorem Ipsum simply ...</p>
    </div>
    <div class="feature__item-content-bottom">
      <img src="images/img9.jpg" alt="" class="img__bg">
    </div>
  </div>
  <div class="feature__item-img">//Note
    <img src="images/img8.jpg" alt="" class="img__bg">
  </div>
</li>

Cấu trúc đã thay đổi nên không thể để 67% 33% như hai cột được nữa nên chúng ta phải code lại. Như đã nói ở phần phân tích thì ta có tỉ lệ ba cột tương ứng là 33% 34% 33%.

Để chọn feature__item cuối cùng thì chúng ta sẽ dùng :last-child và CSS cho phần hình ảnh(cùng một class) và phần nội dung ở giữa lần lượt là.

.feature__item:last-child > .feature__item-img {
  width: 33%;
}
.feature__item:last-child > .feature__item-content {
  width: 34%;
}

“Rất đơn giản” phải không các bạn ?

Còn hai điểm nữa mà mình quên làm một là ở content-top của cột nội dung ở phía dưới có cái hình tam giác như mình đã note mũi tên ở trên hình.

Chúng ta đã tìm hiểu và sử dụng nó ở bài before và after đấy. Các bạn có thể quay lại xem để tham khảo nha. Ở đây mình sẽ dùng :after và code như sau

.feature__item-content-top:after {
  content: "";
  position: absolute;
  bottom: 0;
  left: 50%;
  width: 2rem;
  height: 2rem;
  background-color: white;
  z-index: 10;
  transform: translate(-50%, 50%) rotate(-45deg);
}

Và hai là ở phần chữ trong content-top có border ở dưới một đoạn ngắn mình cũng đã chỉ trên hình cho các bạn thấy đó. Nên chúng ta sẽ lại áp dụng :after tiếp để làm.

.feature__item-text:after {
  content: "";
  position: absolute;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 6rem;
  height: 1px;
  background-color: var(--bg-light);
}

Kết quả là sau một hồi vật vã phân tích và code thì chúng ta đã có kết quả y như mong đợi hehe.

# Lời kết

Phù!!! Cuối cùng cũng xong. Cám ơn các bạn đã đọc một bài viết dài và chi tiết như thế này. 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 xem có giống code của các bạn làm không nhé cũng như để tiếp tục theo dõi phần 4 sẽ ra sắp tới.

Nếu có gì không hiểu hoặc góp ý thì đừng ngại mà bình luận nha. Mình luôn sẵn sàng trả lời. Chúc các bạn một ngày tốt lành.

Subscribe
Notify of
guest

25 Comments
Inline Feedbacks
View all comments
duong
duong

Ad viết về phần hướng dẫn responsive được không ạ? 😀

duong
duong
Reply to  evondev

nice <3

Cường
Cường

Hi vọng bạn làm cả phần responsive nữa !! Cảm ơn nhiều 😀

Hoàng
Hoàng

Thank anh, mong anh ra nhiều bài viết hơn nữa để mọi người cùng tham khảo và học tập.

Minh
Minh

Anh ơi cho em hỏi khi nào thì mình dùng padding hoặc margin để căn văn bản vậy anh? Em thấy anh trong bài lúc dùng padding lúc dùng margin vậy làm sao để biết dùng cái nào vậy anh? Cảm ơn anh vì bài viết

Minh
Minh
Reply to  evondev

Cám ơn anh em cứ thắc mãi. Anh cho em xin hỏi thêm 1 câu nữa, khi em đang viết theo code css của anh có vài chỗ em cứ thắc mắc là khi em xóa 1 thuộc tính thì browser vẫn hiển thị như cũ. Cụ thể lúc em viết cái class feature__item-content thì có thuộc tính height: 100% thì khi em xóa nó đi nó vẫn không làm thay đổi giao diện. Vậy sao mình phải thêm nó vào vậy anh?

Minh
Minh
Reply to  evondev

Ok em cám ơn anh chúc anh có sức khỏe để ra tiếp những bài viết bổ ích như vậy.

Việt Anh
Việt Anh

Hi a, lại là e đây =)) ko biết a nhớ ko, tự dưng e đọc lại và thắc mắc 1 điểm, đó là chỗ phần text trong .feature__item-content-top mà tăng thì làm sao để giữ được tỉ lệ của layout? =D

thanh
thanh

sau những phần làm xong mình thấy có tải code về coi lại thì đa phần download lỗi, bạn coi lại giúp mình,

Hùng
Hùng

Hic, nhìn code của a gọn gàng hết sức. Nội dung hữu ích quá a ơi

hvust
hvust

vấn dề thừa thiếu 1 vài px trong margin vs pading có quan trọng quá ko b

Hiếu
Hiếu

Phần feature__header-desc nó có vài cái tương tự như feature__item-text e thấy người ta khuyên nên thêm cả hai cái vào class rồi mình sửa lại vài cái trong feature__item-text.
Với lại e thấy người ta dùng Block__Element–Modifier cái phần M người ta dùng 2 gạch còn anh dùng 1 gạch, cái này có quan trọng k anh

Hiếu
Hiếu
Reply to  evondev

Tks anh, em hiểu rồi. Em vẫn chưa quen với kiểu đặt tên này lắm :))