One slice of a countdown — a number and the unit it counts. Stacked for the boxed and bar layouts, inline for the pill.
Layout = Stacked | Inline covers both arrangements the system needs: the number above its label in the boxed and bar styles, and the two side by side in the pill. Colour comes from whatever places it — see the note on that in Countdown.Never used on its own. Four instances sit inside every Countdown, one per unit, and the layout that holds them decides whether they stack or run inline.
#time then #unit. Stacked centres the label under the number; Inline sets both at the same size on one line.| State | iOS | Android | Figma Property | Notes |
|---|---|---|---|---|
| Layout=Stacked | N/A | N/A | 25 × 34 | Number above label. Used by the Per and One styles. |
| Layout=Inline | N/A | N/A | hugs content | Number and label on one line at 16px. Used by the Pill style. |
| #time | N/A | N/A | String | Always two digits — a leading zero keeps the row from shifting as the value falls. |
| #unit | N/A | N/A | String | "days" / "hrs" / "mins" / "secs" stacked; "d" / "h" / "m" / "s" inline. |
- The label property is one word. It was
Unit Label, with a space. Property names become parameters, and a space has to be transformed away wherever that happens — the same defect asweek 6on Date Picker - Calendar. It is nowUnitLabel, which also pairs with thehasUnitLabelboolean beside it. C2 · Variant & Property Naming - The component says what it is. It was called
Base Time-Unit Primitive— a description of its place in the architecture rather than of the object. Everything else in the system is named for the thing itself, so it is nowCountdown - Unit, matching the family prefix. C1 · Layer Structure & Naming - The setting has real values. It shipped with Figma's untouched default,
Property 1=Default— a dropdown with one meaningless value. It is nowLayout = Stacked | Inline, where both values do something. C2 · Variant & Property Naming - The label layer is called what it holds.
#blurbbecame#unit. In the naming guidelines a blurb is a short descriptive passage; this layer holds "days". C2 · Variant & Property Naming - The pill stopped abusing the stacked version. Pill used to take the stacked unit, put the whole string into
#timeas "00d", override the type from Bold 20 to SemiBold 16, and leave#unitreading "days" on all four instances just outside the pill's edge. The Inline version replaced all of that — 24 dead layers across the six pill versions are gone. C1 · Layer Structure & Naming
- Raise the muted label colour on filled surfaces if the palette allows. On the bold blue treatment the label renders at roughly 3.2:1 against its box, where 10px text wants 4.5:1. It was raised one tier already. The ceiling is the surface rather than the token — pure white on that blue reaches only about 4.4:1 — so clearing it needs a darker box, which the brand palette does not currently offer. Kept as-is deliberately to preserve the hierarchy between the number and its label. Token
- Let the number and label be separate parameters natively. v1.1: Applied — Property Mapping takes them as two values,
value: Intandunit: EBTimeUnit, never a preformatted string. The enum goes further than the recommendation asked: the platform renders the label from it, so the label follows the device locale instead of shipping whatever a designer typed in Figma. Docs
Two text layers, one colour, no states — the unit is never pressed or disabled on its own. It also paints no surface: whatever sits behind it belongs to the countdown that places it.
| Role | Token | Value |
|---|---|---|
| #time | text/color-text-primary | #005CE5 |
| #unit | text/color-text-primary | #005CE5 |
iOS — Swift Package Manager
.package(url: "https://github.com/AY-Org/eb-ds-ios", from: "1.0.0")
Android — Gradle (Kotlin DSL)
implementation("com.eastblue.ds:countdown:1.0.0")
Import
import EastBlueDS import com.eastblue.ds.countdown.*
Planned API — the native library does not exist yet. The artifact is the Countdown family, not this component: Unit, Promo and Countdown itself all ship in <code>com.eastblue.ds:countdown</code> and import <code>com.eastblue.ds.countdown.*</code>. In practice a team places a Countdown rather than a Unit; this is the piece it renders.
Four properties, in the order the Figma property panel lists them. One shape difference is deliberate and worth knowing: Figma exposes UnitLabel as free text because a designer has to type something to see it, but the native parameter is an enum the platform localises. The string in Figma is what that enum renders in English — passing a string through would put days in front of every user regardless of locale, which is what the Accessibility and Usage Guidelines sections both argue against.
| Figma Property | SwiftUI | Compose |
|---|---|---|
| Layout — Stacked, Inline | layout: EBCountdownUnitLayout = .stacked | layout: EBCountdownUnitLayout = Stacked |
| Value (text) | value: Int — zero-padded on render, so Figma's "00" is what a value of 0 draws | value: Int — same |
| UnitLabel (text) | unit: EBTimeUnit — the platform renders the label. Figma's text is the result, not an input: a typed "days" would hardcode English into every instance | unit: EBTimeUnit — same, resolved against the device locale |
| hasUnitLabel — true, false | showsUnit: Bool = true | showsUnit: Boolean = true |
EBCountdownUnit( value: 0, unit: .days, layout: .stacked )
EBCountdownUnit( value = 0, unit = EBTimeUnit.Days, layout = EBCountdownUnitLayout.Stacked )
EBCountdownUnit( value: 0, unit: .days, layout: .inline )
EBCountdownUnit( value = 0, unit = EBTimeUnit.Days, layout = EBCountdownUnitLayout.Inline )
| Requirement | iOS | Android |
|---|---|---|
| The unit is not announced on its own | .accessibilityHidden(true) — the countdown announces the whole duration | Modifier.clearAndSetSemantics {} on the unit |
| Labels come from the locale | DateComponentsFormatter supplies the unit wording | MeasureFormat with the user's locale |
| Numerals respect the user's numbering system | NumberFormatter rather than string interpolation | NumberFormat.getInstance(locale) |
Do
Pass the number and the unit separately so the platform can localise the label.
Don't
Don't pass a preformatted "00d" string — it can't be translated or re-laid-out.
Do
Keep two digits, including the leading zero.
Don't
Don't let the width change as the value drops; the row will jitter every second.
| ID | Criterion | Status | Notes |
|---|---|---|---|
| C1 | Layer Structure & Naming | Ready | Two text layers, #time and #unit, in both versions. No leftover or hidden layers. |
| C2 | Variant & Property Naming | Ready | Layout = Stacked | Inline. The untouched Property 1 default is gone. |
| C3 | Token Coverage | Ready | Both text layers are bound to text/color-text-primary, confirmed by design. The unit paints no surface of its own, so the contrast of a muted label on a filled box is the placing component's concern, not this one's. |
| C4 | Native Mappability | Ready | Two labels in a stack or a row — trivially mappable, and the split keeps localisation possible. |
| C5 | Interaction State Coverage | Not Applicable | Display only — a countdown is read, not operated. |
| C6 | Asset & Icon Quality | Ready | No assets — type only. |
1 Layout setting × 2 values. Value, UnitLabel and hasUnitLabel are content settings and do not multiply the set.
| Layout | Size | Type | Used by | Node |
|---|---|---|---|---|
| Stacked | 25 × 34 | Primary/Headlines/Segment over Primary/Label/Light/Tiny | Per · One | 5625:35647 |
| Inline | 55 × 16 | Primary/Label/Light/Base throughout | Pill | 7831:111597 |
Unit Label became UnitLabel. Property names become parameters, and a space has to be transformed away wherever that happens — the same defect as week 6 on Date Picker - Calendar. It now pairs with the hasUnitLabel boolean beside it. This is what makes the release a minor rather than a patch.Layout and nothing else; the Figma panel also carries Value, UnitLabel and hasUnitLabel. get_node_info cannot read property definitions, so the gap only surfaced when the property panel was checked directly — worth remembering as a limit of reading the node tree alone.days for d whenever Layout was Inline, and the Properties row documented that swap as a fact with a variants override. Figma has days in both versions — the characters are the same on both nodes. The swap was a preview convenience written up as a specification.text/color-text-primary, confirmed by design.Proxima Soft Bold · 20 / 24 · 0 and its siblings gave way to the styles the layers resolve to: Primary/Headlines/Segment over Primary/Label/Light/Tiny when Stacked, and Primary/Label/Light/Base for both layers when Inline.Layout is the driving property, so Stacked and Inline each get a card, with the three content settings as controls on both.Dimensions, Arrangement and an Alignment whose value read "label centred under the number". Now Height · Width · Padding H · Padding V · Gap · Alignment on each card. Radius is omitted: the unit paints no shape to round.font-family: inherit, so every layer drew in the documentation font — all of them are Primary/*, which is Proxima Soft.gcash/east-blue-ios and com.gcash.eastblue:components:1.0.0, with no Import line. It now cites the Countdown family artifact com.eastblue.ds:countdown:1.0.0 and imports com.eastblue.ds.countdown.*.#time and #unit rather than Value and UnitLabel, and hasUnitLabel was absent. Four rows now, with the type corrected from UnitLayout to EBCountdownUnitLayout.UnitLabel as free text because a designer has to type something to see it; the parameter is unit: EBTimeUnit, an enum the platform localises. Figma's string is what that enum renders in English. The mapping now says so outright rather than presenting a string parameter that would ship "days" to every user — which is what the Accessibility and Usage Guidelines sections already argued against.getSnippet keeps the DEV block following all four properties — including dropping unit for showsUnit: false when the label is switched off.Base Time-Unit Primitive to Countdown - Unit, matching the family prefix.Property 1=Default replaced with Layout = Stacked | Inline.#blurb renamed to #unit, which is what it holds.#time, changed the type style, and left #unit reading "days" outside the pill on all four instances.