FixNeeds Refinement
Service Item Component link

A service tile — icon slot plus label and optional description — used inside the home Menu Grid and customizable shortcut surfaces. Type axis adds a "New" badge, an Add (+) overlay, or a Remove (–) overlay over the icon.

Rename the State typo + split Type into orthogonal slots
Two things to fix. (1) The State axis contains a typo — State=Disbaled on every horizontal/disabled variant. It must be renamed to Disabled. (2) The Type axis bundles "what icon is shown" (Default) with "what modifier overlays the icon" (New / Add / Remove). These are independent — a New shortcut might also be in Remove mode while editing. Split Type into badge: .none | .new and action: .none | .add | .remove so the matrix collapses from 24 variants to a leaner 2 × 3 × 2 × 2 = ~24 with semantic axes (and the invalid combos drop out).
In Context

Used inside the home Menu Grid (the 4×N icon-and-label grid above the bills/transfer shortcuts) and inside the "Customize your home" reordering screen, where Add/Remove overlays appear over the icon during edit mode. Vertical orientation is the dominant home-grid usage; horizontal is reserved for list-style surfaces (e.g. the recent-services drawer).

Cash In New Send + Bills
Live Preview
Properties
Type
State
Orientation
DS Health
Reusable
Partial
Covers both home-grid (vertical) and list-row (horizontal) layouts cleanly. But the Type axis fuses icon modifier and badge into a single value, which limits combinatorial reuse (e.g. "New + Remove" mode is not expressible).
Self-contained
Partial
Owns label / preamble / icon-slot styling but instance-swaps a separate Badge (for New) and an Icon Action (for Add / Remove). Both are external dependencies that drift independently.
Consistent
Fail
State=Disbaled is misspelled in 6 variants (every horizontal/disabled combo). The label's color, the icon-slot fill, and the disabled-state opacity treatment also differ between Inactive and Disabled with no clear rule. C2
Composable
Warn
Icon slot is a true Figma Slot, which is great. But preamble, label, and description text nodes are baked — a consumer can't hide just description while showing preamble.
Behavior
State iOS Android Figma Property Notes
Tap Yes Yes Default state only No Pressed treatment modeled. Tile should respond to touch-down (subtle bg darken or scale 0.97).
Inactive N/A N/A Label dims to #C2CFE5 Used when a service is temporarily unavailable but visible. Conflated visually with Disabled.
Disabled N/A N/A Same visual as Inactive No distinct treatment from Inactive. Should differ (e.g. 40% opacity + pointer-events:none vs. just-dimmed label).
Add (edit-mode) Yes Yes Type=Add overlay Green + circle in top-right of icon. Tappable as a separate target; tap area not annotated.
Remove (edit-mode) Yes Yes Type=Remove overlay Red – circle in top-right. Same tap-area issue as Add.
New badge N/A N/A Type=New Static red "New" badge. Should decay after first tap or after N days — no spec.
Open Issues
  • State=Disbaled is misspelled across 6 variants. Every horizontal/disabled variant in the component set has State=Disbaled (typo) in its component name. This propagates to Code Connect prop names and gets baked into native APIs. Rename to Disabled in Figma. C2 · Variant & Property Naming
  • Type axis bundles unrelated concerns. Type = Default | New | Add | Remove conflates a content-presence flag (New = badge present), an action overlay (Add / Remove), and the neutral case (Default). These should be two orthogonal axes: badge: .none | .new and action: .none | .add | .remove. Today, a "New" tile can't simultaneously be in Remove mode during edit. C2 · Variant & Property Naming
  • Inactive and Disabled render the same. No visual difference between State=Inactive and State=Disabled — both dim the label to a muted blue. Functionally they should differ: Inactive = visible but not tappable for business reasons; Disabled = visible but not tappable for state reasons (form invalid, feature flag off). Pick distinct treatments. C5 · Interaction State Coverage
  • No Pressed state. Tiles are primary tap targets but have no pressed feedback. Add a transient treatment (8% darken on icon-slot bg, or a 0.97 scale) on touch-down. C5 · Interaction State Coverage
  • Icon Action overlays are 12 × 12 but tap-area unannotated. The + and – Icon Action circles are tiny (12 px). They're separate tap targets from the tile itself but no minimum tap area is annotated. Native devs default to wrapping the whole tile, which kills the edit-mode UX. C5 · Interaction State Coverage
  • Description text node is baked. Both #label and #description are always present in the layer tree. Consumers can't hide just description (most home-grid usages don't need it). Should be an optional text slot. C4 · Native Mappability
  • Code Connect mappings not registered. Blocked on the State typo fix and the Type-axis split. C7 · Code Connect Linkability
Design Recommendations
  • Fix the State=Disbaled typo. Rename in Figma across all 6 affected variants. Verify no consumer screens reference the old prop value via Code Connect. Rename
  • Split Type into badge + action axes. Target schema: badge: .none | .new + action: .none | .add | .remove. Each is an optional slot/overlay; combinations like "New + Remove" become expressible without new variants. Total variant count stays similar but covers the full matrix instead of the linear union. Property
  • Distinguish Inactive from Disabled. Inactive: label dims to main/text/color/disabled, still tappable (e.g. opens an "unavailable" sheet). Disabled: 40% opacity on the whole tile + pointer-events: none + aria-disabled. Document the semantic difference. State
  • Promote description to an optional content slot. Most home-grid usages only show the label. Make description a slot that consumers fill when needed, instead of a baked text node with placeholder copy that ships in production by mistake. Slot
  • Add a Pressed state. Touch-down: 8% darken on icon-slot bg + 0.97 scale on the whole tile. Touch-up: snap back. Improves perceived responsiveness on the home grid. State
  • Annotate Icon Action tap-area. The 12 px + and – circles need a 44 × 44 (iOS) / 48 dp (Android) hit-area extension so users can reliably tap them in edit mode. Document this on the component. A11y
  • Document edit-mode interaction model. Add and Remove overlays only appear when the parent (Menu Grid in edit mode) is reordering. Document the parent contract — the Service Item shouldn't need to know about the parent's edit state directly. Docs
Types
Default
DES DEV

Vertical: 64 × 72 — preamble (12 tall) above icon, 48 × 48 circular icon slot, then label + description. Horizontal: 120 × 64 — icon left, label/description right, preamble below. Type adds a "New" badge, a green +, or a red – over the icon. State dims the label for Inactive / Disabled.

Properties
Type
State
Orientation
Properties
Type Default
State Default
Orientation Vertical
Colors
Icon-slot bg #F6F9FD
Label #072592
New badge bg #E11744
New badge text #FFFFFF
Add overlay #16A34A
Remove overlay #E11744
Layout
Tile 64 × 72
Icon slot 48 × 48 (pill / radius 99999)
Icon → label gap 4
Preamble 12 tall (above icon)
Modifier badge 29 × 12 (top-right, offset −6 top)
Action overlay 12 × 12 (top-right, offset −6 top)
Typography
Label Proxima Soft Bold · 12 / 12 · +0.5
Preamble Proxima Soft Semibold · 10 / 12 · +0.25
Description BarkAda Semibold · 10 / 12
Label by State

Label color is the only thing State changes. Today Inactive and Disabled render identically — flagged as C5.

Role Token DefaultInactiveDisabled
Label service-item/label/{state} #072592 #C2CFE5 #C2CFE5
Icon-slot bg service-item/icon/bg #F6F9FD #F6F9FD #F6F9FD
Property Mapping

After the Type-axis split, the native API has two independent slot params (<code>badge</code>, <code>action</code>) instead of one fused Type enum.

Figma PropertySwiftUICompose
Type=Default (no badge, no action) (no badge, no action)
Type=New badge: .new badge = Badge.New
Type=Add action: .add action = Action.Add
Type=Remove action: .remove action = Action.Remove
State=Default / Inactive / Disabled state: .default | .inactive | .disabled state: ServiceItemState
Orientation=Vertical / Horizontal orientation: .vertical | .horizontal orientation: Orientation
Icon (slot) icon: Image icon: @Composable () -> Unit
#label label: String label: String
#description description: String? description: String?
#preamble preamble: String? preamble: String?
SwiftUI
ios/Components/ServiceItem/EBServiceItem.swift
Jetpack Compose
android/components/serviceitem/EBServiceItem.kt
Accessibility
RequirementiOSAndroid
Tile role Wrap as Button; .accessibilityLabel(label + (description.map { ", \($0)" } ?? "")). Use Modifier.clickable + Role.Button; contentDescription = label + ", " + description.
New badge .accessibilityValue("new") so VoiceOver reads "Send, new". Append "new" to stateDescription.
Add / Remove overlay Separate accessibility element — .accessibilityLabel("Add Send to home"). Hit area extended via .contentShape(Rectangle()) + larger frame. Wrap as separate IconButton with explicit contentDescription; Modifier.minimumInteractiveComponentSize().
Disabled .disabled(true) + .accessibilityValue("disabled"). enabled = false; stateDescription = "disabled".
Inactive Still tappable; .accessibilityHint("Currently unavailable"). Tappable; contentDescription appends "currently unavailable".
Criteria Scorecard
ID Criterion Status Notes
C1 Layer Structure & Naming Needs Refinement Clean layer naming inside (preamble / content / Icon / border). The 24-variant matrix is the concern, not the layer tree.
C2 Variant & Property Naming Requires Rework State=Disbaled typo + Type axis bundles badge with action overlays.
C3 Token Coverage Needs Refinement Icon-slot fill and label color bound. New badge + Add/Remove action overlays use ad-hoc colors (#E11744, #16A34A) without registered tokens.
C4 Native Mappability Needs Refinement Maps cleanly to a SwiftUI/Compose tile after the Type-axis split; description should be optional.
C5 Interaction State Coverage Requires Rework No Pressed state. Inactive and Disabled render identically. Action-overlay tap-area unannotated.
C6 Asset & Icon Quality Ready Icon slot accepts any vector instance — clean Slot architecture.
C7 Code Connect Linkability Not Mapped Blocked on State typo + Type-axis split.
Variants Inventory (24 total)

Type × State × Orientation = 4 × 3 × 2 = 24 variants. Note 6 of them have State=Disbaled misspelled. Once Type splits into badge + action, the matrix collapses to ~8 production variants + slot-driven combinations.

#TypeStateOrientationSizeNode
1DefaultDefaultVertical64 × 7220210:2442
2NewDefaultVertical64 × 7220210:2450
3AddDefaultVertical64 × 7220210:2460
4RemoveDefaultVertical64 × 7220210:2469
5DefaultInactiveVertical64 × 7220210:2478
6NewInactiveVertical64 × 7220210:2486
7AddInactiveVertical64 × 7220210:2496
8RemoveInactiveVertical64 × 7220210:2505
9DefaultDisabledVertical64 × 7220210:2634
10NewDisabledVertical64 × 7220210:2642
11AddDisabledVertical64 × 7220210:2652
12RemoveDisabledVertical64 × 7220210:2661
13DefaultDefaultHorizontal120 × 6420210:2514
14NewDefaultHorizontal120 × 6420210:2523
15AddDefaultHorizontal120 × 6420210:2534
16RemoveDefaultHorizontal120 × 6420210:2544
17DefaultInactiveHorizontal120 × 6420210:2554
18NewInactiveHorizontal120 × 6420210:2563
19AddInactiveHorizontal120 × 6420210:2574
20RemoveInactiveHorizontal120 × 6420210:2584
21DefaultDisabledHorizontal120 × 6420210:2594
22NewDisabledHorizontal120 × 6420210:2613
23AddDisabledHorizontal120 × 6420210:2603
24RemoveDisabledHorizontal120 × 6420210:2624
1.0.0 — 2026-05-19Major
Initial Assessment · node 20210:2441
Component assessed — 24 variants across Type × State × Orientation. Powers the home Menu Grid and the customize-home edit mode. Documented
Initial
Verdict: Fix — Rename the State=Disbaled typo, split Type into orthogonal badge + action axes, distinguish Inactive from Disabled. Open
Family
C2 — State=Disbaled typo — Misspelled across 6 horizontal/disabled variants. Will propagate to Code Connect props unless fixed in Figma. Open
C2
C2 — Type axis bundles badge + action — Default / New / Add / Remove conflates content presence with overlay action. Split into badge + action. Open
C2
C5 — Inactive vs Disabled — Render identically today. Need distinct visual + interaction treatments. Open
C5
C5 — Tap-area + Pressed — No Pressed state; Icon Action overlays are 12 px with no annotated hit-area extension. Open
C5
C7 — Code Connect — Not registered. Blocked on the State typo and Type-axis split. Open
C7