A 2D grid of Service Item tiles used for top-level service navigation. Column (2–5) sets the tiles across, Row (1–5) the tiles down.
Column=4, Row=2) — string values are the correct approach here, since Figma variant properties cannot be typed as integers. Interaction states are owned by the Service Item child and are assessed there, not on this layout container. Code Connect mappings are left open for engineering to register.Contexts are illustrative. Final screens will reference actual GCash patterns. Menu Grid sits on the dashboard as the primary service shortcut surface — typically <code>Column=4, Row=2</code> (8 services) on the home screen.
Column=5), 4 row gap, and 6 radius.Column counts tiles across, Row counts tiles down. Values are plain numbers across all 20 variants.SLOT, so each cell is overridable without detaching.| State | iOS | Android | Figma Property | Notes |
|---|---|---|---|---|
| Default | Yes | Yes | Column × Row | Menu Grid is a layout container and has no interaction states of its own. Tap, pressed and disabled behaviour belong to the Service Item child. |
- Code Connect mappings not registered. Left open for engineering. The property surface is stable and linkable —
ColumnandRowmap straight onto a lazy grid's column count and item count. C7 · Code Connect Linkability
- Variant values are plain numbers. The earlier set used pseudo-numeric strings (
Row="by 4"). The rebuild usesColumn=4, Row=2. String values are the correct and only option here — Figma variant properties cannot be typed as integers — so the prior recommendation to switch to integer props has been withdrawn. C2 · Variant & Property Naming - Service Item is a first-class DS component. Previously flagged as a child of Menu Grid only. It is now published standalone with its own
State/Orientation/Badge/Actionaxes, and Menu Grid instances it rather than redefining tiles. C1 · Layer Structure & Naming - Container metrics are uniform across all 20 variants. Two
Row=5variants previously broke the shared metrics —Column=5, Row=5was 352 wide against 336 everywhere else, andColumn=2, Row=5used 60-tall tiles against 64. Both were corrected in Figma. Every variant is now a 336-wide container at a 4 row gap, with 64-tall tiles atColumn=2and 72-tall tiles atColumn=3–5. C1 · Layer Structure & Naming - Icons come through a slot. Each tile's icon is a Figma
SLOT, not baked artwork, so cells are swappable without detaching the instance. C6 · Asset & Icon Quality
- Document that
Column=2switches the tile to horizontal. AtColumn=2the Service Item child renders atOrientation=Horizontal(158 × 64, icon left of the label);Column=3–5render vertical. Menu Grid exposes no orientation property, so a native lazy grid will not reproduce the switch on its own — it has to be stated in the property mapping. No Figma change required. Docs
The default dashboard layout — 2 rows × 4 columns = 8 services. Used on the home dashboard for primary service shortcuts.
EBMenuGrid(items: services, columns: 4) Menu Grid owns only the container surface. Every colour inside a tile — icon, label, description — belongs to Service Item and is documented there.
| Role | Token | Value |
|---|---|---|
| Container bg | bg/color-bg-main | #FFFFFF |
| Border (weak) | border/color-border-weak | #E5EBF4 |
Expanded grid for "All Services" sheets — 4 rows × 4 columns = 16 services. Used on category pages or full-list views.
EBMenuGrid(items: services, columns: 4) Measured from the component set. Column=5 tightens the horizontal padding and column gap to fit five 64px tiles inside the same 336px container.
| Role | Token | Value |
|---|---|---|
| Container width | — | 336px (fixed) |
| Padding (horizontal) | space/space-8 | 8px · 6.4px at Column=5 |
| Padding (vertical) | space/space-8 | 8px |
| Row gap | space/space-4 | 4px |
| Column gap | space/space-4 | 4px · 0.8px at Column=5 |
| Corner radius | radius/radius-2 | 6px |
| Tile width | — | 158 · 104 · 77 · 64 by Column |
| Tile height | — | 64px at Column=2 · 72px at Column=3–5 |
| Icon slot | — | 48 × 48 |
Maximum density — 5 rows × 5 columns = 25 services. Use sparingly; label legibility tightens at this density.
EBMenuGrid(items: services, columns: 4) 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:menu-grid:1.0.0") }
| Figma Property | SwiftUI | Compose |
|---|---|---|
| Row="by N" | rows: Int | rows: Int |
| Column="by N" | columns: Int | columns: Int |
| Service Item (instance) | items: [EBServiceItem] | items: List<EBServiceItem> |
// Default dashboard grid — 2 rows × 4 columns EBMenuGrid(columns: 4, items: services) // Service Item child EBServiceItem( icon: Image("send-money"), label: "Send Money", action: { /* tap */ } )
// Default dashboard grid — 2 rows × 4 columns EBMenuGrid( columns = 4, items = services ) // Service Item child EBServiceItem( icon = painterResource(R.drawable.send_money), label = "Send Money", onClick = { /* tap */ } )
| Requirement | iOS | Android |
|---|---|---|
| Tap target | Service Item is 64+ px wide × ~64 px tall — meets HIG 44pt minimum | Meets Material 48dp minimum |
| Accessibility label | .accessibilityLabel(label) on each item | contentDescription = label |
| Grid semantics | Container exposes grid traits via LazyVGrid | LazyVerticalGrid announces row/column position |
| Disabled state | Currently undefined — needs token + .disabled(true) handling | Currently undefined — needs token + enabled = false |
Do
Use Row=2 × Column=4 for the primary dashboard surface — 8 services is the established home pattern.
Don't
Use Row=5 × Column=5 unless density is essential — 25 cells reduces label legibility.
Do
Pair Menu Grid with a section heading or container card so users understand the grouping.
Don't
Mix icon styles within a single grid — keep all Service Item icons in the same vector style.
| ID | Criterion | Status | Notes |
|---|---|---|---|
| C1 | Layer Structure & Naming | Ready | Every child is a named Service Item instance. No Frame/Group debris in the component set. |
| C2 | Variant & Property Naming | Ready | Column = tiles across, Row = tiles down — the conventional reading. Values are plain numbers. Figma variant properties are string-only, so numeric strings are the correct representation. |
| C3 | Token Coverage | Not Applicable | Menu Grid owns only the container surface and its spacing. Every tokenised colour inside a tile belongs to Service Item and is audited there — the same delegation as C5. |
| C4 | Native Mappability | Ready | Maps to LazyVGrid / LazyVerticalGrid(GridCells.Fixed(Column)) with Column × Row items. The tile orientation switch at Column=2 must be carried in the API — see the Docs recommendation. |
| C5 | Interaction State Coverage | Not Applicable | Menu Grid is a layout container with no states of its own. Tap and disabled behaviour is assessed on Service Item. |
| C6 | Asset & Icon Quality | Ready | Icons are delivered through a Figma SLOT on each tile rather than baked artwork. |
| C7 | Code Connect Linkability | Not Mapped | No CLI mappings registered yet — left open for engineering. |
4 Column × 5 Row = 20 variants. Column counts tiles across, Row counts tiles down. Every variant is a 336-wide container of Service Item instances at a 4 row gap.
| Column | Row range | Tiles | Tile size | Item orientation | Count |
|---|---|---|---|---|---|
| Column = 2 | 1 – 5 | 2 – 10 | 158 × 64 | Horizontal | 5 |
| Column = 3 | 1 – 5 | 3 – 15 | 104 × 72 | Vertical | 5 |
| Column = 4 | 1 – 5 | 4 – 20 | 77 × 72 | Vertical | 5 |
| Column = 5 | 1 – 5 | 5 – 25 | 64 × 72 | Vertical | 5 |
View full Column × Row breakdown (20 rows)
| Column | Row | Tiles | Container | Node |
|---|---|---|---|---|
| Column = 2 | Row = 1 | 2 | 336 × 80 | 5973:70192 |
| Column = 2 | Row = 2 | 4 | 336 × 148 | 5973:70186 |
| Column = 2 | Row = 3 | 6 | 336 × 216 | 5973:70177 |
| Column = 2 | Row = 4 | 8 | 336 × 284 | 5973:70150 |
| Column = 2 | Row = 5 | 10 | 336 × 352 | 5973:70162 |
| Column = 3 | Row = 1 | 3 | 336 × 88 | 5973:70202 |
| Column = 3 | Row = 2 | 6 | 336 × 164 | 5973:70195 |
| Column = 3 | Row = 3 | 9 | 336 × 240 | 5973:70342 |
| Column = 3 | Row = 4 | 12 | 336 × 316 | 5973:70329 |
| Column = 3 | Row = 5 | 15 | 336 × 392 | 5973:70313 |
| Column = 4 | Row = 1 | 4 | 336 × 88 | 5973:70282 |
| Column = 4 | Row = 2 | 8 | 336 × 164 | 5973:70256 |
| Column = 4 | Row = 3 | 12 | 336 × 240 | 5973:70227 |
| Column = 4 | Row = 4 | 16 | 336 × 316 | 5973:70112 |
| Column = 4 | Row = 5 | 20 | 336 × 392 | 5973:70129 |
| Column = 5 | Row = 1 | 5 | 336 × 88 | 5973:70276 |
| Column = 5 | Row = 2 | 10 | 336 × 164 | 5973:70265 |
| Column = 5 | Row = 3 | 15 | 336 × 240 | 5973:70240 |
| Column = 5 | Row = 4 | 20 | 336 × 316 | 5973:70206 |
| Column = 5 | Row = 5 | 25 | 336 × 392 | 5973:70287 |
Row=5 dimensions corrected — Column=5, Row=5 (5973:70287) narrowed from 352 to 336 to match every sibling, and Column=2, Row=5 (5973:70162) moved from 60-tall to 64-tall tiles, taking the container from 332 to 352. All 20 variants now share the same container width and per-column tile height.
Resolved5973:70111 in GCash Design System · 2026 Working File, replacing 18320:14332 in Sticker Sheets v2. Still 20 variants: 4 Column × 5 Row, all individually verified.
DocumentedColumn=4, Row=2 replaces the earlier Row="by 4" strings. The prior recommendation to move to integer props is withdrawn: Figma variant properties are string-only, so numeric strings are the correct representation.
ResolvedRow=5 variants break the shared metrics — Column=5, Row=5 is 352 wide against 336 everywhere else, and Column=2, Row=5 uses 60-tall tiles against 64.
OpenColumn 2–5 × Row 1–5 with plain numeric labels, the preview renders columns across instead of down, and a readout surfaces the Column=2 → Orientation=Horizontal switch.
DocumentedRow="by 4", Column="by 4". Native takes Int; the string prefix forces parsing.
Opendashboard/service-item/color/active/{icon,label} defined. Other states must be improvised.
Open