Nghệ thuật dựng component trong thực tế-Phần 2
Tags: html, css, component, react
Published: 2022-05-11 07:46:12

Bài viết được dịch từ:
https://ishadeed.com/article/building-real-life-components/

### Các khu vực của component
Để triển khai một component chuẩn, trước tiên chúng ta cần suy nghĩ kỹ về HTML trước. Trong trường hợp này, chúng ta có hai khu vực của **component** có nhiều biến thể đó là hình đại diện và khu vực nội dung.

#### Ảnh đại diện

![Image avatar-1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n0bbrlc89gen7ph7o5z5.png)

Để code HTML cho phần avatar, trước tiên chúng ta cần hiểu các trạng thái của nó. Dưới đây là các biến thể có thể có:

- Hình đại diện duy nhất
- Hình đại diện duy nhất có trạng thái trực tuyến
- Nhiều hình đại diện cho trò chuyện nhóm
- Nhiều hình đại diện có trạng thái trực tuyến

Xem xét đoạn HTML sau, chúng ta muốn đảm bảo rằng `.card__avatar` có thể xử lý tất cả các biến thể ở trên.

```
<div class="card">
  <div class="card__avatar"></div>
  <div class="card__content">
    <!-- Name, message, badge.. -->
  </div>
</div>
```

##### Hình đại diện duy nhất

Hãy tập trung vào biến thể đầu tiên, đó là một hình đại diện duy nhất. Hình đại diện phải có đường viền bên trong (hoặc bóng trong) để làm cho nó trông giống như một vòng tròn ngay cả khi hình đại diện được sử dụng toàn màu trắng.

![Image avatar-2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/imvnm651vjhr0zkzfrqu.png)

Trong CSS, không thể áp dụng `box-shadow` (inner) cho phần tử `img`. Chúng ta có hai lựa chọn:

- Sử dụng thêm một thẻ `div` có đường viền trong suốt.
- Sử dụng thẻ `svg`.

Mục tiêu nếu đường viền bên trong là hiển thị đường viền xung quanh hình đại diện trong các trường hợp:

- hình đại diện toàn màu trắng ở chế độ ánh sáng
- hình đại diện toàn màu đen ở chế độ tối

![Image inset-shadow](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6vqu72t9eeloc01ues1y.png)

Không có đường viền bên trong, sử dụng hình đại diện toàn màu trắng sẽ làm cho nó hòa trộn với nền gốc của nó. Điều tương tự cũng áp dụng cho chế độ tối. Đây là hình ảnh trực quan về những gì sẽ xảy ra có và không có đường viền bên trong.

![Image inner-shadow-2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7br9ok0wchp4u2auk1vl.png)

##### Sử dụng thẻ div cho đường viền bên trong

Đối với giải pháp này, một phần tử bổ sung (chính là thẻ div) được đặt hoàn toàn phía trên hình ảnh với độ mờ là 0,1.

```
<div class="card__avatar">
  <img src="assets/shadeed.jpg" alt="" />
  <div class="border"></div>
</div>
```

```
.card__avatar {
  position: relative;
}

.card__avatar img {
  width: 56px;
  height: 56px;
  border-radius: 50%;
}

.border {
  position: absolute;
  width: 56px;
  height: 56px;
  border: 2px solid #000;
  border-radius: 50%;
  opacity: 0.1;
}
```
Giải pháp này đúng, nhưng có một số hạn chế mà tôi sẽ giải thích sau đây.

##### Sử dụng thẻ svg

Đối với giải pháp này, chúng tôi sẽ sử dụng một thẻ `svg`. Ý tưởng là sử dụng mặt nạ hình tròn cho hình đại diện và phần tử `circle` cho đường viền bên trong. SVG hoạt động tuyệt vời cho điều này.

```
<svg role="none" style="height: 56px; width: 56px">
  <mask id="circle">
    <circle cx="28" cy="28" fill="white" r="28"></circle>
  </mask>
  <g mask="url(#circle)">
    <image
      x="0"
      y="0"
      height="100%"
      preserveAspectRatio="xMidYMid slice"
      width="100%"
      xlink:href="/assets/shadeed.jpg"
      style="height: 56px; width: 56px"
    ></image>
    <circle class="border" cx="28" cy="28" r="28"></circle>
  </g>
</svg>
```

```
.border {
  stroke-width: 3;
  stroke: rgba(0, 0, 0, 0.1);
  fill: none;
}
```

Cả hai giải pháp đều hoạt động được khi chỉ xây dựng với ví dụ hình đại diện duy nhất. Tuy nhiên, mọi thứ bắt đầu trở nên thú vị hơn khi chúng ta thêm yếu tố trực tuyến.

##### Hình đại diện duy nhất có trạng thái trực tuyến

Ở chế độ ánh sáng, hình tròn màu xanh lục có viền trắng. Tuy nhiên, ở chế độ tối, điều này nên được bị khỏi chính hình đại diện. Nói cách khác, nên sử dụng mặt nạ.

![Image avatar-3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ym1dpmrlfi8cdi8zwh9v.png)

Làm thế nào chúng ta có thể làm điều đó? Nếu chúng ta sử dụng giải pháp `svg` cho hình đại diện duy nhất, điều này có thể được giải quyết dễ dàng bằng cách sử dụng mặt nạ SVG.

```
<svg role="none" style="height: 56px; width: 56px">
  <mask id="circle">
    <!-- [1] -->
    <circle cx="28" cy="28" fill="white" r="28"></circle>
    <!-- [2] -->
    <circle cx="48" cy="48" fill="black" r="7"></circle>
  </mask>
  <!-- [3] -->
  <g mask="url(#circle)">
    <image
      x="0"
      y="0"
      height="100%"
      preserveAspectRatio="xMidYMid slice"
      width="100%"
      xlink:href="/assets/shadeed.jpg"
      style="height: 56px; width: 56px"
    ></image>
    <circle class="border" cx="28" cy="28" r="28"></circle>
  </g>
</svg>
```

Hãy để tôi chia nhỏ đoạn code SVG trên:

1. Một vòng tròn để che hình đại diện thực tế.
2. Một hình tròn nhỏ để cắt từ góc dưới bên phải của hình đại diện.
3. Một group chứa `image` và `circle` cho đường viền bên trong trong suốt.

Dưới đây là hình ảnh giải thích cách hoạt động: 

![Image avatar-4](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/atyciqkje2gt6ukfolrf.png)

Cùng với đó, đây là đoạn code HTML.

```
<div class="card__avatar">
  <svg role="none" style="height: 56px; width: 56px">
    <mask id="circle">
      <circle cx="28" cy="28" fill="white" r="28"></circle>
      <circle cx="48" cy="48" fill="black" r="7"></circle>
    </mask>
    <g mask="url(#circle)">
      <image
        x="0"
        y="0"
        height="100%"
        preserveAspectRatio="xMidYMid slice"
        width="100%"
        xlink:href="/assets/shadeed.jpg"
        style="height: 56px; width: 56px"
      ></image>
      <circle class="border" cx="28" cy="28" r="28"></circle>
    </g>
  </svg>
  <div class="badge"></div>
</div>
```

```
.card__avatar {
  position: relative;
  display: flex;
  margin-right: 12px;
}

.badge {
  position: absolute;
  right: 3px;
  bottom: 3px;
  width: 10px;
  height: 10px;
  background: #5ad539;
  border-radius: 50%;
}
```

**Khi một component như thế này cần phải thích ứng với cả bố cục sáng và tối, bạn nên sử dụng các biến CSS để xử lý nhằm lưu trữ các giá trị của màu cần thay đổi.**

```
:root {
  --primary-text: #050505;
  --secondary-text: #65676b;
  --bg-color: #fff;
}

html.is-dark {
  --primary-text: #e4e6eb;
  --secondary-text: #b0b3b8;
  --bg-color: #242526;
}

.card {
  background-color: var(--bg-color);
}

.card__title {
  color: var(--primary-text);
}

.card__subtitle {
  color: var(--secondary-text);
}
```

### Hết phần 2