Mở đầu — vì sao bài này quan trọng
Hãy tưởng tượng bạn đang thiết kế một ứng dụng ngân hàng số. Trong toàn bộ sản phẩm, bạn có khoảng 40 nút bấm: nút "Chuyển tiền", nút "Xác nhận", nút "Hủy", nút "Đăng nhập"... Nếu mỗi nút bạn vẽ lại từ đầu — chọn màu, chọn bo góc, chọn padding, chọn font — thì điều gì sẽ xảy ra? Nút thì cao 44px, nút thì 46px. Nút thì bo góc 8px, nút thì 6px. Khi sếp yêu cầu "đổi màu chủ đạo từ xanh sang tím", bạn phải sửa tay 40 chỗ, và chắc chắn sót vài chỗ.
Đó chính là lý do component design ra đời. Component (thành phần) là những "viên gạch" có thể tái sử dụng — bạn thiết kế một lần, dùng lại khắp nơi, sửa một chỗ thì cả sản phẩm cập nhật theo. Nghe thì đơn giản, nhưng thiết kế một component tốt — vừa linh hoạt, vừa nhất quán, vừa dễ bảo trì — lại là một kỹ năng cốt lõi phân biệt người mới vào nghề với một Product Designer thực thụ.
Ở các bài trước, bạn đã nắm được Figma nâng cao (Bài 11) và cách dùng Variables & Modes (Bài 12). Bài này, chúng ta tập trung vào tư duy khi xây từng viên gạch một: làm sao để một component "làm tốt một việc", có những biến thể (variants) nào, và phơi ra những thuộc tính (properties) gì để người khác dùng lại. Đây là nền móng. Ở Bài 14 chúng ta sẽ ráp các viên gạch theo Atomic Design, còn Bài 15 sẽ đóng gói chúng thành một design system hoàn chỉnh. Nhưng nếu viên gạch xây sai ngay từ đầu, cả tòa nhà sẽ lung lay.
Khái niệm cốt lõi
Component là gì và tại sao nó khác một bản thiết kế thường
Một bản thiết kế tĩnh (static design) là hình ảnh "đông cứng" của một màn hình. Một component, ngược lại, là một đối tượng sống: nó có quy tắc bên trong, có các trạng thái, có những phần được phép thay đổi và những phần bị khóa. Trong Figma, đó là sự khác biệt giữa một nhóm (group) các shape rời rạc và một Component thật sự với các Properties (thuộc tính) phơi ra ngoài bảng điều khiển bên phải.
Khi bạn tạo component đúng cách, người dùng lại nó (có thể là đồng nghiệp designer khác, hoặc chính bạn ba tháng sau) chỉ cần kéo "instance" (bản sao) ra, rồi gõ nội dung và chọn vài tùy chọn — chứ không phải vọc vào bên trong để sửa từng layer.
Nguyên tắc 1 — Single Responsibility (Trách nhiệm đơn nhất)
Đây là nguyên tắc quan trọng nhất, vay mượn từ lập trình: một component chỉ nên làm tốt một việc.
Một nút bấm (Button) thì chỉ làm nhiệm vụ "kích hoạt một hành động". Nó không nên kiêm luôn việc hiển thị avatar, không nên có ô nhập liệu bên trong, không nên chứa logic của một thẻ sản phẩm. Khi một component cố ôm đồm nhiều việc, nó trở nên cồng kềnh, khó hiểu, và mỗi lần ai đó muốn dùng lại đều phải gỡ bỏ những phần thừa.
Cách kiểm tra nhanh: nếu bạn phải dùng từ "VÀ" để mô tả công dụng component ("đây là cái thẻ hiển thị thông tin sản phẩm VÀ cho phép nhập số lượng VÀ có nút đặt hàng VÀ hiện đánh giá"), đó là dấu hiệu component đang gánh quá nhiều. Hãy tách nó thành nhiều component nhỏ hơn rồi ghép lại (composition).
Nguyên tắc 2 — Variants (Biến thể)
Một component thực tế hiếm khi chỉ có một hình dạng duy nhất. Cùng là nút bấm, bạn cần:
- Style (kiểu): Primary (nút chính, nổi bật), Secondary (nút phụ), Tertiary/Ghost (nút mờ, viền hoặc chỉ chữ), Destructive (nút nguy hiểm như "Xóa", thường màu đỏ).
- Size (kích thước): Small, Medium, Large — để phù hợp với những bối cảnh dày đặc hay thoáng đãng khác nhau.
- State (trạng thái): Default (mặc định), Hover (rê chuột), Pressed/Active (đang nhấn), Disabled (bị khóa), Loading (đang xử lý).
Bí quyết: hãy nghĩ về variants theo các trục độc lập (axes). Style, Size, State là ba trục riêng. Nếu bạn có 4 style × 3 size × 5 state, về lý thuyết là 60 tổ hợp — nhưng bạn không cần vẽ tay 60 cái nếu cấu trúc layer của bạn tốt và dùng được auto layout cùng variables.
Nguyên tắc 3 — Properties (Thuộc tính)
Properties là những "cần gạt" bạn phơi ra để người dùng tùy chỉnh instance mà không phá vỡ cấu trúc. Figma có bốn loại property chính:
- Text property: cho phép đổi nội dung chữ (ví dụ nhãn của nút) ngay từ bảng bên phải.
- Boolean property: bật/tắt một phần tử (ví dụ: "Hiện icon bên trái?" — true/false).
- Instance swap property: cho phép thay một component con bằng component khác (ví dụ: đổi icon "mũi tên" thành icon "tải xuống").
- Variant property: chọn biến thể như đã nói ở trên.
Đặt tên và tổ chức
Một component tái sử dụng được phải tìm thấy được. Quy ước đặt tên phân cấp bằng dấu gạch chéo rất phổ biến: Button/Primary/Large, Input/Text/Error, Card/Product/Default. Figma sẽ tự gom chúng thành cây thư mục. Tên rõ ràng giúp đồng đội tìm đúng viên gạch trong vài giây thay vì lục tung cả file.
Tình huống thực tế
Ví dụ 1 — Tiki và bài toán nút bấm "lạc trôi"
Giả sử một nhóm thiết kế tại một sàn thương mại điện tử lớn như Tiki, giai đoạn đầu khi sản phẩm phình to nhanh. Có thời điểm họ rà soát lại và phát hiện trong codebase và file Figma tồn tại hơn 20 kiểu nút bấm khác nhau — chỉ riêng nút "Mua ngay" đã có 5 biến thể màu cam hơi lệch sắc độ nhau, cái thì #FF6B00, cái #FF5722, cái #F57224.
Nguyên nhân: mỗi designer phụ trách một luồng (trang chủ, trang sản phẩm, giỏ hàng, thanh toán) tự vẽ nút của riêng mình. Không ai sai cả — nhưng tổng hợp lại thì sản phẩm trông thiếu nhất quán, và đội ngũ frontend phải viết hàng chục đoạn CSS gần-giống-nhau.
Cách họ xử lý: Lập một component Button duy nhất với các variant (Primary/Secondary/Ghost × Small/Medium/Large) và một state Loading có sẵn. Màu sắc được rút về một biến color/brand/primary thống nhất (đúng tinh thần Variables ở Bài 12). Sau đó họ thay thế dần các nút cũ bằng instance của component mới.
Bài học: Số lượng biến thể của một component phải là quyết định có chủ đích, không phải hệ quả của việc thiếu phối hợp. Một component Button được thiết kế tốt giúp tiết kiệm hàng trăm giờ bảo trì về sau, và quan trọng hơn, làm cho trải nghiệm người dùng đồng nhất.
Ví dụ 2 — Startup fintech và chiếc Input "ôm đồm"
Một startup fintech tại TP.HCM (khoảng 8 người, đang gấp rút ra MVP) có một bạn designer dựng component ô nhập liệu (Input field). Vì muốn tiện, bạn ấy nhồi tất cả vào một component khổng lồ: label phía trên, ô nhập, icon bên phải, dòng helper text, dòng error text, VÀ cả nút "Gửi OTP" nằm bên trong.
Hậu quả xuất hiện khi đội cần một ô nhập không có nút, một ô nhập có hai icon, một ô nhập trong form mà error hiển thị kiểu khác. Mỗi lần như vậy, họ phải detach (gỡ rời) component để sửa — và một khi detach, instance đó mất liên kết, không còn cập nhật theo component gốc nữa. Sau hai tuần, file Figma đầy những input "mồ côi" không đồng bộ.
Cách sửa: Họ tách thành các viên gạch nhỏ tuân thủ single responsibility — Input chỉ lo việc nhập liệu (với boolean property cho icon trái/phải, text property cho placeholder, variant cho state Default/Focus/Error/Disabled). Còn nút "Gửi OTP" tách thành component Button riêng, rồi ghép cạnh Input khi cần. Form OTP giờ là một sự lắp ghép, không phải một khối liền.
Bài học: Component càng cố "làm sẵn mọi thứ" thì càng kém linh hoạt. Hãy thiết kế những viên gạch nhỏ, đúng trách nhiệm, rồi ghép lại. Linh hoạt đến từ composition, không phải từ việc nhồi nhét.
Ví dụ 3 — Grab và state Loading bị bỏ quên
Trong một ứng dụng gọi xe/giao đồ ăn như Grab, người dùng nhấn nút "Đặt xe" và hệ thống cần vài giây để khớp tài xế. Nếu nút không có trạng thái Loading, người dùng sốt ruột bấm liên tục → gửi nhiều request trùng → có thể đặt nhầm 2 chuyến.
Giả sử nhóm thiết kế ban đầu chỉ làm component Button với các state Default/Hover/Disabled mà quên state Loading. Mỗi màn hình lại tự chế một cách hiển thị "đang xử lý" khác nhau: chỗ thì spinner xoay, chỗ thì đổi chữ thành "Đang xử lý...", chỗ thì làm mờ nguyên màn hình.
Cách xử lý: Bổ sung hẳn một state Loading vào component Button (spinner + khóa tương tác). Từ đó mọi nút trong app khi bấm đều hiển thị loading nhất quán, và quan trọng là tự động bị disable để chống double-submit.
Bài học: Khi liệt kê variants/states cho một component, đừng chỉ nghĩ tới trạng thái "đẹp đẽ lý tưởng". Hãy nghĩ tới cả vòng đời tương tác: chờ đợi, lỗi, bị khóa. (Chúng ta sẽ đào sâu loading và error state ở Bài 48–49, nhưng ngay từ tầng component, bạn đã phải chừa chỗ cho chúng.)
Hướng dẫn từng bước
Hãy cùng dựng một component Button đúng chuẩn từ đầu:
- Xác định trách nhiệm. Viết ra một câu: "Button này dùng để kích hoạt một hành động." Nếu bạn không viết được trong một câu không có chữ "và", hãy tách nhỏ trước khi vẽ.
- Vẽ trạng thái nền tảng (base). Dựng một nút Primary/Medium/Default. Dùng auto layout để padding tự co giãn theo nội dung — đây là điều bắt buộc để component linh hoạt theo độ dài chữ. Bind màu, bo góc, padding vào variables thay vì gõ số cứng.
- Khai báo các trục variant. Xác định các trục độc lập: Style (Primary/Secondary/Ghost/Destructive), Size (S/M/L), State (Default/Hover/Pressed/Disabled/Loading). Tạo Component Set và đặt tên property rõ ràng.
- Phơi ra properties. Thêm Text property cho nhãn nút. Thêm Boolean property "Show icon left" và "Show icon right". Thêm Instance swap cho icon để người dùng đổi biểu tượng tùy ý.
- Đặt tên có hệ thống.
Button/Primary/Medium. Đảm bảo cây thư mục trong panel Assets gọn gàng, dễ tìm.
- Viết ghi chú sử dụng ngắn. Ngay cạnh component, ghi một dòng: "Dùng Primary cho hành động chính, tối đa 1 nút Primary mỗi màn hình." Việc này nối tiếp sang Bài 18 về documentation, nhưng ngay bây giờ một dòng note cũng đủ cứu đồng đội.
- Kiểm thử bằng cách dùng thật. Kéo vài instance ra, thử đổi nhãn, đổi size, bật/tắt icon, chuyển sang Loading. Nếu mọi tùy chỉnh đều mượt mà mà không cần detach — bạn đã có một component tốt.
Lỗi thường gặp & mẹo
Lỗi 1 — Over-engineering (làm quá phức tạp). Tạo ra component với 15 boolean property cho mọi tình huống tưởng tượng. Kết quả: bảng điều khiển rối loạn, không ai hiểu. Mẹo: chỉ phơi ra property khi có nhu cầu thực tế lặp lại ít nhất 2–3 lần. Đừng thiết kế cho tương lai chưa tới.
Lỗi 2 — Lạm dụng Detach. Mỗi lần detach là cắt đứt liên kết với component gốc → instance đó không bao giờ nhận cập nhật nữa. Mẹo: nếu bạn thấy mình hay phải detach, đó là tín hiệu component thiếu một property hoặc variant cần thiết. Hãy nâng cấp component gốc thay vì gỡ rời.
Lỗi 3 — Bùng nổ tổ hợp variant. Nhồi quá nhiều trục vào một component set khiến số tổ hợp tăng theo cấp số nhân và file nặng nề. Mẹo: tách trục "ít liên quan" thành component riêng, hoặc dùng instance swap thay cho variant khi phù hợp.
Lỗi 4 — Quên trạng thái Disabled/Error/Loading. Như ví dụ Grab. Mẹo: mỗi component tương tác, hãy luôn tự hỏi: "Khi bị khóa thì sao? Khi lỗi thì sao? Khi đang chờ thì sao?"
Lỗi 5 — Đặt tên tùy tiện (Button 2, Frame 47 copy). Mẹo: đặt tên ngay khi tạo, theo cấu trúc phân cấp. Tên là tài liệu rẻ nhất bạn có thể viết.
Mẹo vàng: Hãy thiết kế component như thể bạn sẽ trao nó cho một designer mới vào nhóm dùng mà không được giải thích gì. Nếu bạn ấy dùng đúng ngay lập tức, component của bạn đã đạt chuẩn "reusable building block".
Bài tập thực hành
- Audit nút bấm. Mở một app bạn hay dùng (Momo, ZaloPay, Shopee...). Chụp màn hình và liệt kê tất cả các kiểu nút bạn thấy. Nhóm chúng lại: có thật sự cần nhiều kiểu vậy không, hay đó là sự thiếu nhất quán?
- Dựng component Button. Trong Figma, làm theo 7 bước ở phần Hướng dẫn để tạo một Component Set Button hoàn chỉnh: tối thiểu 3 style × 3 size × 4 state, có text property, boolean icon, và instance swap cho icon.
- Thiết kế component Input theo single responsibility. Tạo một Input field chỉ lo việc nhập liệu, với các state Default/Focus/Error/Disabled, boolean cho icon, helper/error text. Sau đó ghép nó với component Button để tạo một form đăng nhập — chứng minh sức mạnh của composition.
- Thử thách refactor. Lấy một component "ôm đồm" (tự bạn dựng cố tình hoặc tìm trong file cũ) và tách nó thành các viên gạch nhỏ tuân thủ trách nhiệm đơn nhất. Ghi lại bạn đã tách thành mấy component và vì sao.
Tóm tắt
Component design là kỹ năng biến những bản vẽ tĩnh thành những "viên gạch" sống, tái sử dụng được — nền móng của mọi design system. Ba nguyên tắc cốt lõi cần khắc cốt ghi tâm:
- Single responsibility: mỗi component làm tốt đúng một việc; linh hoạt đến từ ghép nối, không phải nhồi nhét.
- Variants: suy nghĩ theo các trục độc lập (style, size, state) và đừng quên những state "không đẹp" như disabled, error, loading.
- Properties: phơi ra đúng những cần gạt cần thiết (text, boolean, instance swap, variant), giấu đi sự phức tạp bên trong.