A structured table primitive for displaying rows of label/value or column-aligned data.
Collapse the family and rethink Table on mobile
The 3-component setup (Table + Table - Item + Table - Label) hardcodes a column-count variant matrix that doesn't scale. On mobile, tabular data almost always renders as a vertical stack of label/value pairs — which is exactly what the existing Inline Text component already does. Evaluate whether Table should ship as a DS primitive at all, or be reserved for true data-dense desktop contexts while mobile screens compose
List + Inline Text rows instead. If Table stays, collapse the matrix into a single data-driven row (columns: [Column]) with named leading / trailing slots and optional per-row icon.In Context
Sticker sheet shows Table instances stacked on a Template Screen to build a static 6-row pattern — 1 header row + 5 content rows. No scroll, no sort, no selection.
Live Preview
Header
Column
Column
Column
Properties
type
no. of columns
icon (header only)
DS Health
Reusable
Warn
Works for the narrow case of 2–4 equal-width columns with a left label and right descriptions. Breaks for mixed widths, amounts, badges, or sortable columns — all common table use cases.
Self-contained
Warn
Header row carries bg, border, label/column typography; content row carries label + description. But column count is locked at build time — consumers can't add or remove columns without detaching.
Consistent
Fail
no. of columns is a string enum with a period in the name — collides with C2 naming conventions. Three orphan sibling components (Table, Table - Item, Table - Label) when one data-driven row would suffice. C2Composable
Warn
Items and Labels are declared as reusable atoms but the parent Table doesn't slot them — it re-implements the label and description directly. No real composition.
Open Issues
- Three orphan components instead of one data-driven row. Table, Table - Item, and Table - Label are published as separate components but only Table is consumed — Item and Label are never placed directly. Should collapse into a single row primitive that accepts a columns array. C1 · Layer Structure & Naming
-
no. of columnsuses string enum with a period in the property name. Should be an integercolumnCount— the period breaks code-friendly naming and the string "2"/"3"/"4" can't interpolate to a data-driven row count. C2 · Variant & Property Naming - No native mobile primitive matches this layout. iOS SwiftUI
Tableis macOS/iPad-only; Material Compose has no Table primitive. On phones, tabular data is a vertical stack of label/value pairs (Inline Text) or a horizontally-scrollable list. Current 360px-fixed rows don't adapt. C4 · Native Mappability - No interaction state coverage. Rows have no hover, pressed, focused, selected, or disabled states. If rows are ever tappable (drill-down into a row detail), there's no visual affordance. C5 · Interaction State Coverage
- Header icon is a raw placeholder circle.
icon=yesvariants render a hardcoded#C2C6CF24px circle with no instance swap or named slot. Consumers have no documented way to set the icon. C6 · Asset & Icon Quality - Code Connect mappings not registered. Blocked until the family consolidates and native decision lands. C7 · Code Connect Linkability
Design Recommendations
- Reconsider whether Table belongs in a mobile-first DS. GCash ships on phones where tables almost never render as true HTML-style tables. The sticker sheet pattern screen is doing what Inline Text already does — a stack of label / value rows. Two cleaner paths: (a) remove Table from core DS, publish usage guidance pointing to
List+Inline Textfor label/value data; (b) keep Table but scope it to genuine multi-column data (transaction history, scheduled payments, etc.) and rebuild it as a data-driven row. Family - Collapse the three-component family into one row primitive. Merge Table, Table - Item, and Table - Label into a single Table Row component with a
columnsslot array and arolevariant (header/content). Eliminates theno. of columnsvariant explosion — 9 records collapse into 2 + slot content. Published node count drops from 3 components / 14 variants to 1 component / 2 variants. Property - Rename
no. of columnsto an integer (or drop it entirely). The period in the property name breaks C2 naming conventions. If the data-driven restructure lands, column count is inferred from thecolumnsarray and the prop disappears. Otherwise rename tocolumnCountwith integer values. Rename - Replace the header icon placeholder with a named slot. Declare an
iconSlot on the header-role row so consumers drop in any 24px icon component. Drop theicon=yes/noboolean — slot presence signals intent. Maps cleanly to@ViewBuilder/@Composableslot. Slot - Add row interaction states. If rows are ever tappable (drill-down row detail, sortable columns), publish
default/pressed/selected/disabledstate variants. If rows stay display-only, document that explicitly so native devs wrap in a non-interactive container. State - Introduce shared label/value token set with Inline Text. Table label and Inline Text label serve the same semantic role — "a thing and its value". Aligning tokens (
main/table/color/labelwithmain/inline-text/*) reduces drift and supports cross-component theming. Token
Types
Header row
DES DEV
Subtle-bg row with bottom border. Primary-bold label on the left, semibold columns on the right. Optional 24px icon above each column.
Properties
Columns
Icon
Properties
Columns 4
Icon no
Colors
Surface bg #F6F9FD
Border #E5EBF4
Label #0A2757
Column #0A2757
Icon placeholder #C2C6CF
Layout
Height 37
Padding H 24
Padding V 8 / 12
Icon size 24 × 24
Typography
Header style Primary/Label/Light/Base
Header font Proxima Soft Semibold · 16 / 16 · +0.25
Body style Primary/Label/Small
Body font Proxima Soft Bold · 14 / 14 · +0.25
Colors by State
| Role | Token | Default | Pressed | Disabled |
|---|---|---|---|---|
| Header bg | main/table/color/bg-subtle | #F6F9FD | – | – |
| Content bg | main/table/color/bg | #FFFFFF | – | – |
| Row border | main/table/color/border | #E5EBF4 | – | – |
| Label / column text | main/table/color/label | #0A2757 | – | – |
| Description text | main/table/color/description | #6780A9 | – | – |
| Header icon placeholder | — (hardcoded) | #C2C6CF | – | – |
Content row
DES DEV
White bg. Bold 12px label on the left, BarkAda Semibold description columns on the right.
Properties
Columns
Properties
Columns 4
Colors
Surface bg #FFFFFF
Border #E5EBF4
Label #0A2757
Description #6780A9
Layout
Height 56
Padding H 24
Padding V 12
Typography
Header style Primary/Label/Light/Base
Header font Proxima Soft Semibold · 16 / 16 · +0.25
Body style Primary/Label/Small
Body font Proxima Soft Bold · 12 / 12 · +0.5
Layout
| Role | Token | Value |
|---|---|---|
| Row width (fixed) | — | 360px |
| Header height (icon=no) | — | 37px |
| Header height (icon=yes) | — | 65px |
| Content row height | — | 56px |
| Horizontal padding | space/space-24 | 24px |
| Header pt / pb | space/space-8, space/space-12 | 8 / 12px |
| Content py | — | 12px |
| Column gap | — | 16px |
| Label width | — | 99px min |
| Header icon size | — | 24 × 24px |
| Icon → column gap | space/space-2 | 2px |
Installation
Planned API
iOS — Swift Package Manager
// In Xcode: File → Add Package Dependencies "https://github.com/AY-Org/eb-ds-ios"
Android — Gradle (Kotlin DSL)
dependencies { implementation("com.eastblue.ds:table:1.0.0") }
Property Mapping
| Figma Property | SwiftUI | Compose |
|---|---|---|
| type=header/content | role: .header / .content | role = EBTableRowRole.Header / Content |
| no. of columns (drop) | columns: [Column] | columns: List<Column> |
| icon (slot) | leadingIcon: Image? | leadingIcon: @Composable (() -> Unit)? |
| Label here text | label: String | label: String |
| Description text (xN) | columns: [Column] | columns: List<Column> |
SwiftUI
ios/Components/Table/EBTableRow.swift
Jetpack Compose
android/components/table/EBTableRow.kt
Usage Snippets Planned API
Usage
// Option A — dedicated row primitive (if Table stays in DS) VStack(spacing: 0) { EBTableRow( role: .header, label: "Header", columns: ["Column", "Column", "Column"] ) ForEach(rows) { row in EBTableRow( role: .content, label: row.label, columns: row.values ) } } // Option B — compose with Inline Text (recommended for mobile) VStack(spacing: 12) { EBInlineText(label: "Amount", value: "₱1,000.00") EBInlineText(label: "Reference", value: "0000 0123 4567") EBInlineText(label: "Date", value: "Apr 22, 2026") }
// Option A — dedicated row primitive (if Table stays in DS) Column { EBTableRow( role = EBTableRowRole.Header, label = "Header", columns = listOf("Column", "Column", "Column") ) rows.forEach { row -> EBTableRow( role = EBTableRowRole.Content, label = row.label, columns = row.values ) } } // Option B — compose with Inline Text (recommended for mobile) Column(verticalArrangement = Arrangement.spacedBy(12.dp)) { EBInlineText(label = "Amount", value = "₱1,000.00") EBInlineText(label = "Reference", value = "0000 0123 4567") EBInlineText(label = "Date", value = "Apr 22, 2026") }
Accessibility
| Requirement | iOS | Android |
|---|---|---|
| Tabular semantics | On iPad/macOS use Table; on iPhone use accessibilityElement(children: .combine) per row so each reads as "Label, value, value". | Wrap in Modifier.semantics { collectionInfo = CollectionInfo(rowCount, columnCount) }. |
| Header vs content row | Use .accessibilityAddTraits(.isHeader) on header rows. | Use Modifier.semantics { heading() } on header rows. |
| Column headers without visible text | If icon-only header columns exist, provide .accessibilityLabel. | Set contentDescription on the icon slot. |
| Row-level tap | If rows become tappable, wrap row in Button with .accessibilityHint. | Wrap in Modifier.clickable with role = Role.Button. |
Criteria Scorecard
| ID | Criterion | Status | Notes |
|---|---|---|---|
| C1 | Layer Structure & Naming | Requires Rework | 3 orphan components (Table, Table - Item, Table - Label) where 1 data-driven row would suffice. |
| C2 | Variant & Property Naming | Requires Rework | no. of columns uses string enum with a period; should be integer or dropped. |
| C3 | Token Coverage | Needs Refinement | Label / bg / border bound to main/table/*. Header icon placeholder uses hardcoded #C2C6CF. |
| C4 | Native Mappability | Requires Rework | No native iOS/Android mobile primitive. Needs a mobile rethink — list of label/value pairs vs true desktop table. |
| C5 | Interaction State Coverage | Requires Rework | No hover / pressed / selected / disabled states. |
| C6 | Asset & Icon Quality | Requires Rework | Header icon is a raw placeholder circle with no slot or instance swap. |
| C7 | Code Connect Linkability | Not Mapped | Blocked until family consolidation and native decision land. |
Variants Inventory (0 total)
Table ships 9 variants. Family siblings Table - Item (3 variants) and Table - Label (2 variants) are declared but never placed directly in screens — only Table is consumed.
| type | no. of columns | icon | Height | Node ID |
|---|---|---|---|---|
| header | 2 | no | 37px | 47:323220 |
| header | 3 | no | 37px | 47:323222 |
| header | 4 | no | 37px | 47:323224 |
| header | 2 | yes | 65px | 47:323221 |
| header | 3 | yes | 65px | 47:323223 |
| header | 4 | yes | 65px | 47:323225 |
| content | 2 | no | 56px | 47:325867 |
| content | 3 | no | 56px | 47:325868 |
| content | 4 | no | 56px | 47:325869 |
1.0.0 — April 2026Major
Initial Assessment · node 47:326260
Family assessed — 3 published components (Table / Table - Item / Table - Label) with 9 + 3 + 2 variants. Only Table is consumed in screens. Documented
InitialThree orphan components — Table - Item and Table - Label exist but are never placed directly. Recommend collapsing into one data-driven row. Open
C1 Openno. of columns naming — String enum with a period in the property name. OpenNo native mobile primitive — Mobile tables are almost always stacks of label/value pairs (Inline Text) — reconsider whether Table belongs in the DS at all. Open
C4 OpenNo interaction states — No hover / pressed / selected / disabled state variants. Open
C5 OpenHeader icon is a raw placeholder — Hardcoded
C6 Open#C2C6CF circle with no slot or instance swap. OpenCode Connect mappings — Not registered. Open
C7 Open