Thiết kế Figma Responsive Table với Autolayout và Min/Max Width
Gần đây mình Design Table khá nhiều. Có khám phá ra cách tiếp cận để thiết kế Responsive Table qua nhiều Breakpoints chính xác và dễ dàng hand-off với Developers. Xin được chia sẻ với mọi người.
Morning morning,
Mình tin rằng anh em Designer trong quá trình làm việc - hoặc ít hoặc nhiều - cũng sẽ thiết kế Data Table một vài lần. Đặc biệt nhiều hơn đối với anh em làm việc trong các Domain như Finance, Logistic, Healthcare.
Data Table (ngắn gọn là Table) tổ chức lượng thông tin lớn được tốt hơn.
Khả năng tra cứu, sắp xếp thông tin bằng Sort và Filter.
Trực quan, dễ dàng so sánh thông tin.
Qua bài viết mình xin được chia sẻ hướng tiếp cận và thiết kế một Table một cách chi tiết nhất.
P/s: Link Template Figma mình để ở cuối bài.
CÁC CÁCH MÌNH DESIGN TABLE TRƯỚC ĐÂY
Trước tiên, mình xin giới thiệu một vài cách làm Table trước đây mình đã từng làm.
Cách 1: Không có Cell, Width của Data được Fixed (dựa theo chiều dài) và Gap between = Auto.
Đây là cách thiết kế Table đơn giản nhất. Tuy nhiên trong lúc làm thì mình thấy khó áp dụng Atomic vì có nhiều Data chỉ là chữ, ngắn dài đa dạng. Chỉ có thể tạo Component để tái sử dụng đối với một số Data nhất định thôi.
Ưu điểm:
Thiết kế rất simple, nhanh, phù hợp với các project gấp.
Phần Visual nhìn ổn áp.
Nhược điểm:
Sau khi hand off thì Front-end phải tự chia Cell + thuộc tính cho các Cell, Row.
Tại mỗi breakpoint có thể FE làm vẫn đúng, tuy nhiên Designer khó kiểm soát thiết kế khi Responsive đến các Width khác nhau.
Khó tái sử dụng.
Mình đánh giá cách này rất đơn giản, rất phù hợp với làm UI Kit hoặc Showcase.
Tuy nhiên về bản chất thiết kế để hand off tốt cho Front-end thì chưa đúng. Nên mình tiếp tục upgrade lên một cách khác.
Cách 2: Thiết kế sử dụng Cell. Cell có Width = Fixed hoặc Fill (tùy vào loại Data). Gap = 0.
Như ví dụ trên thì Column thứ nhất và thứ 2 là Fixed Width, các Column còn lại là Fill. Đối với project có các breakpoint lệch nhau lớn thì phần Visual sẽ bị lệch.
Hiện tại Figma và UntiledUI đang hướng users thiết kế Table theo cách này, nhưng tiếp cận theo Column. (mình sẽ giải thích vì sao mình lại tiếp cận theo Row mà không phải Column ở bên dưới).
Ưu điểm:
Đã có thể tạo Componets cho từng Cell, Row để tái sử dụng.
Hand off đã ổn áp. Front-end đã có thể dựng được chính xác Table mà không cần tự define thêm các kích thước hoặc thuộc tính nào khác.
Designer có thể control được thiết kế, có thể kiểm tra trực tiếp trên thiết kế (bằng cách kéo các Row dài ra) để xem behavior thực tế của Table. Có thể expected code giống thiết kế 100%.
Nhược điểm:
Cần có nhiều Breakpoints với độ lệch không quá lớn để space giữa các Cell không quá chênh lệch khi làm Responsive.
Chỉ áp dụng được đối với Table có số lượng Column không quá nhiều và phải tương đối đồng đều. Nếu Data nhiều và khác nhau thì các Column sẽ phải Fixed để set Width hợp lý. Vì nhiều Column Fixed nên khi Responsive thì chỉ có một ít cột còn lại là Responsive được.
Mình follow cách làm này trong một khoảng thời gian dài, và đã khá hài lòng.
Cho đến khi mình gặp một Project có nhiều Table, Data đa dạng hơn.
Lúc đầu Project cần tiến độ để dựng MVP nên chỉ define ít Breakpoint. Vấn đề xuất hiện vì khoảng cách giữa 2 Breakpoint lớn, sau khi dựng thực tế xong thì Table bị mất cân đối.
Mình lại tiếp tục trao đổi với anh em Front-end. Lần này thì mình biết thêm về các thuộc tính được sử dụng trong Table, qua đó phát triển cách thiết kế một cách tốt hơn xíu nữa để thích nghi với các loại Table phức tạp hơn.
Big thanks to Khiêm Lê - OTSV đã sharing mọi lúc mọi nơi cho kẻ ngoo dốt này 💪
TỪNG BƯỚC THIẾT KẾ RESPONSIVE DATA TABLE VỚI FIGMA
Minh sẽ lấy một ví dụ cụ thể để mọi người tiện theo dõi:
Một Table quản lý users.
3 breakpoint 1440px, 1200px, 1024px. (tức là đối với width lớn hơn 1440px thì Fixed và Align Center, nhỏ hơn 1024px thì Horizontal Scroll)
Tập trung vào cách xây dựng Table, các Behavior liên quan như Validation, hiển thị lỗi, Row expand, History, Artchitech, … mình xin không đề cập vì sẽ quá dài.
BƯỚC 1: TẠO DATA CELL
Mình bắt đầu bằng việc tạo một Data Cell.
Các bước sẽ là:
1/ Tạo Autolayout cho Data, set Padding và Alignment:
Tùy vào style của dự án thì Cell có thể cao thấp rộng hẹp khác nhau, mình recommend Padding Top = Bottom = 12-18px, từ 64-76px cho Height
Check Data thực tế hoặc ước lượng Width cho các Cell để set trước.
Ví dụ mình có một Data Cell chứa text là tên của các Cảng biển, mình check độ dài trung bình là bao nhiêu, độ dài max là bao nhiêu và có nhiều không. Khi đặt vào mỗi Data Cell thì mình sẽ chọn được kích thước hợp lý.
2/ Tạo component:
Tiltle thiết kế riêng một Component. Vì Title rất ít thay đổi và mỗi khi thay đổi hầu như là đổi hết cả hệ thống. Nên Cell cho Title sẽ làm chung cho tất cả các Table trong project. Dùng Boolean Property để Show/Hide các Icon Sort, Filter.
Có thể dùng Background, Font style hoặc Text color để tách biệt phần Title. Để khi lướt nhanh qua Table có thể dễ phân biệt được Title và phần dữ liệu (Better hierarchy :D)
Mỗi loại Data Cell sẽ có Component riêng tương ứng. Không làm chung thành một Master Component.
Vì sao mình không dựng Master Component (Component chứa hết các Data Cell) mà lại tạo riêng?
Đối với các Project nhỏ, Components/System chung file với file thiết kế (tức là không phải Publish qua lại giữa Component và file Design) thì không ảnh hưởng nhiều lắm, thời gian sync không tốn bao nhiêu.
Tuy nhiên, đối với Project lớn hơn có file Component/System riêng, khi update một Variant Data Cell trong Master Component và Publish từ file Component/System thì file Design sẽ phải update hết tất cả các Variant Data Cell (Visual sẽ không thấy được, nhưng nó chạy ngầm bên dưới, tốn thêm time để sync).
Vậy tạo Component riêng cho từng loại Data Cell sẽ giúp:
Khi update một Data Cell bất kỳ, thì chỉ có các Variant tương ứng sync ⇒ Nhanh hơn.
Không dùng được Component Properties để đổi nhanh giữa các Data Cell thì sao?
Mình naming, dùng Swap Instance để đổi giữa các Data Cell. Các bước swap Data Cell giữa 2 cách là tương đương nhau (đều click vào dropdown để select loại Data Cell cần đổi)
Ngoài ra:
Có thể dùng Stroke để làm divider (một hoặc nhiều cạnh tùy trường hợp).
Cố gắng thu thập thuộc tính của Cell đó, để mình có thể chuẩn bị các Cell Component thật đầy đủ. Một số thuộc tính mà mình thường gặp:
BƯỚC 2: TẠO RESPONSIVE ROW VÀ COMPONENT
Vì sao mình lại tiếp cận thiết kế Component theo Row mà không phải theo Column?
Mình đã thiết kế Table theo cả 2 hướng, có một vài điểm trong lúc làm mình nhận thấy:
Về cách thiết kế, cách làm Responsive, tính linh hoạt khi Update/Edit: 2 hướng tiếp cận là tương đương về các bước làm và độ phức tạp.
Tuy nhiên, có 2 điểm khác biệt lớn nhất:
1/ Việc quản lý thiết kế:
Đối với cách làm theo Row thì tất cả State + Behavior được “gom nhóm” vào một Master component. Khi cần Update/Eeditdit thì thuận tiện hơn vì tính tập trung. (Nội dung, Title và các State mỗi bảng đa số là có sự liên kết, ràng buộc với nhau, ít có trường hợp chỉ sửa nội dung Row mà không sửa Title, hoặc sửa cấu trúc State Default mà không sửa Hover ?!)
Làm theo Column thì mỗi Column là một component (để tái sử dụng), các thành phần của một Table (ở đây là từng Column) sẽ bị rời rạc.
2/ Tính flexible:
Đối với Data có cấu trúc khác nhau, ví dụ có Cell chứa 2 loại thông tin, Cell chứa 1 loại thông tin, Cell chứa Avatar, … thì Height mỗi Cell khác nhau, khi setup một Table bắt buộc phải tự set Fixed Height cho Data Cell.
Trường hợp Data Cell chứa Free text như Note/Summary/Comment, trong cùng một Data Cell thì text có lúc ngắn, có lúc dài, có lúc có, có lúc không. Dẫn đến mất tính linh hoạt khi Fixed Height mỗi Cell.
Do vậy nên mình thấy cách tiếp cận theo Row là tối ưu hơn.
Quay lại bước 2, tiếp theo là các bước tạo Component cho Row và setup để Responsive.
1/ Tạo Row cho mỗi Breakpoint.
Mỗi Data Cell chia đều vào từng Row. Width của Data Cell = Fixed và nên lớn dần tương ứng các breakpoint lớn hơn. Spacing giữa các Data Cell = 0px.
Height của Data Cell = Fill. Height của Row = Hug content.
Setup như trên sẽ giúp các Row luôn ôm theo nội dung của Data Cell. Đồng thời các Data Cell có Height ngắn hơn cũng được Fill hết vào Row.
Set Min/Max Width cho từng Row để Responsive.
Min Row là kích thước Row tại Breakpoint đó
Max Row là kích thước Row tại Breakpoint lớn hơn liền kề.
Set Min/Max Width cho từng Data Cell để Responsive.
Min bằng Width hiện tại của Data Cell tại Breakpoint đó
Max bằng Width của Data Cell ở Breakpoint lớn hơn liền kề.
Sau đó chọn Width = Fill cho các Data Cell. Các Data Cell không đổi kích thước thì chọn Fixed.
Kiểm tra Interactive cho Row bằng cách kéo các Row ở Breakpoint nhỏ dài dần ra.
Tạo các trạng thái khác cho Row như Hover, Selected, Expand, …
Tạo một Master Component cho tất cả các Row vừa setup xong.
Dùng một Master Component giúp chuyển Properties nhanh, khi cần update Table thì các Row nằm cùng ở 1 chỗ, dễ quản, giảm nhầm lẫn.
Khi setup theo cách này, Row ở breakpoint nhỏ sẽ responsive đến Breakpoint liền kề sẽ có Width bằng đúng với Width tại Breakpoint liền kề đó.
TIPS:
Khi Update Table thêm/xóa một Data Cell nào đó:
Đổi tất cả Data Cell từ Fill sang Fixed.
Xóa Min/Max tại mỗi Data Cell
Thêm/Xóa.
Cân chỉnh lại Width của các Data Cell.
Set Min/Max cho Data Cell như trên.
Setup Properties cho Component mình ưu tiên trực quan và nhanh > nhỏ gọn. Đối với cùng 1 Master component, mình có 2 cách để setup Properties như sau:
Phương án 1: dùng 3 Properties là Type, Breakpoint, Show icon cho các states.
Phương án 2: cùng số lượng states nhưng có đến 5 Properties.
Mặc dù phương án 2 có số lượng Properties nhiều hơn, nhưng khi dùng chỉ cần nhìn vào bảng Properties (hình 2.2) và set ngay. Trực quan và nhanh hơn so với phương án 1.
Đối với một project tầm Medium size hoặc Big size, một xíu details nhỏ này cũng giúp mình làm được nhanh hơn.
Anh em Front-end check Min Max ở Dev mode như bên dưới.
Checklist: Một số thuộc tính của Row mà mình thường gặp
BƯỚC 3: TẠO VÀ HOÀN THIỆN TABLE
Tạo Table cho một Breakpoint trước.
Bổ sung các Elements liên quan (Tiêu đề cho Table, số lượng Item, Pagination, Action Buttons, …)
Fill thông tin Sample, thông tin này sẽ giúp mọi người dễ “cảm” được Table hơn so với nếu dùng thông tin lặp lại, hoặc là Lorem ipsum.
Nếu tạo các Breakpoint khác trước thì sau đó mình phải tốn nhiều effort hơn cho việc fill data vào cả 3 Tables.
Vì các Variant trong Table đã được naming, nên khi đổi giữa các state sẽ không bị mất Data.
Tiêp tục làm cho các breakpoint khác theo các bước:
Copy Table
Điều chỉnh Width của Section đến breakpoint liền kề.
Set lại Margin cho Section nếu có (vd đối với 1440px là 40px, 1200px là 32px, 1024px là 24px)
Chọn Table, Enter để chọn hết các Row (Select Children), Bên bảng Properties chọn breakpoint tương ứng.
Anh em có thể thấy sau khi mình điều chỉnh Width của section về với breakpoint nhỏ hơn thì các Row không Fill vào Table được vì đã set Min Max ⇒ dễ dàng phát hiện bug design :D
Tiếp tục đến khi xong cho cả 3 breakpoints.
TIPS
Đối với các Data Cell dạng Free text, set Max Height của text đó tùy theo số dòng mong muốn (vd tối đa 3 dòng, set Max Height = line height x 3). Khi Text bị truncate thì nên chuẩn bị UI cho State Hover + Tooltip để đọc thông tin ngay tại Table.
Điều chỉnh Padding để cân bằng hơn: Vì Margin của Section đối với các breapoint là khác nhau, nên về Table mình cũng nên cân chỉnh Padding đối với Cell đầu tiên và cuối cùng để vừa mắt hơn.
Sau khi tạo hết bảng cho từng breakpoint rồi mà muốn Select/Update một số data nào đó ⇒ Dùng multiple select để chọn nhanh các Cell/Row tương ứng ở các Breakpoints (Ctrl + Alt + A & vì đã Naming và Structure các Table tương đồng nhau rồi). Sau đó Update/Edit phần Content
Đối với Table view trên mobile có thể làm Horizontal scroll hoặc chuyển các Row thành dạng Card.
Trạng thái Hover cho mỗi Row: dùng color hoặc size để chỉ thị rằng có thể nhấn vào.
Xài Google Sheets Sync (Figma Plugin) để Import Data from Excel vào Table thật nhanh.
KẾT LUẬN
Đây là cách hiện tại mình xây Table, sẽ tốn chút thời gian setup ban đầu nhưng khi bắt đầu chạy được một vài Table rồi thì có thể tái sử dụng các component, tốc độ làm sẽ nhanh hơn, hand off nhẹ nhàng hơn.
Tuy nhiên, tùy từng loại dự án mình vẫn sẽ có lúc mình chọn làm theo các cách cũ:
Nếu làm Project UI kit, Showcase mình vẫn sẽ đi theo cách không dùng Data cell (cách 1). Hoặc loại dự án tập trung vào các nội dung khác, phần Table đơn giản hơn, số lượng Data không quá nhiều mình sẽ chọn đi theo cách 2.
Hy vọng bài viết giúp mọi người thiết kế được một Table vừa ý, hoặc ít nhất lượm lặt được gì đó trong bài viết và áp dụng được vào Project mọi người đang chạy.
Big thanks mọi người vì đã đọc tới đây!
.
.
Link Template Figma để mọi người tiện theo dõi.
Research chính từ 3 nguồn: Sharing from Figma Config 2022 (Diego González), Smart Interface Design Patterns course (Vitaly Friedman), How to Create Data Tables in Figma - UntitleUI (Jordan Hughes)