Updated: 2026-08-27 08:24 ET
Audited: 2026-06-26
Underline Button in Figma — 18px height, border-radius:38px) that indicates
expand/collapse affordance. The card itself is a tappable button (cursor:pointer). The "Comms" tab
uses a comms variant of the notification card (different content structure — separate spec pending).
ALPAMobile.Presentation/Components/Notifications/FlightNotificationCard.razor
(CSS prefix alpa-fsnc-* in alpa-components.css), validated live on the
iOS simulator against the working Figma file. Deliberate deviations from this spec's target API,
each with its reason:
NotificationFlightStatusViewModel — the card binds
NotificationCenterMessage directly and derives route/date/timestamp via
FlightNotificationText, because the payload carries the route only inside
message prose (see the grouping
design-gap page §1). The spec's ViewModel remains the target shape if the backend
ever fields route/flight number.IconUrl not implemented — the design's icon slot is itself a
generic placeholder (icon-generic, flat #d9d9d9 ellipse); rendered as drawn
until design assigns per-status/airline glyphs.open variant
(5054:13777) draws one expanded element: a status row with the flight number (Futura
Medium 16px) and a rounded severity pill (radius 24, yellow/500
#f7bb0d for "Delayed 34m", 12px navy text) — and that is all the card
renders, per the series rule: match the design exactly unless an annotation states
otherwise (none does here). The stopgap card's message text, sent time (labelled
Sent — the payload's server-side sentDateTime; device receipt is
never captured), flight
reference and history link were removed from the production card 2026-08-12 but are
retained behind ShowAlternateDetailView — rendered only
when Notifications is entered from the readiness board (?showcase=true),
so the design team can compare on-device what the drawn card gives up. Production
entries (push tap, bell, nav) always render design-exact; there the Flight Updates
screen (/notifications/flight/{id}) is the only path to that content,
reached by push deep link or the readiness scenarios row — the drawn card has no link
slot. Non-delayed severities keep the invented chip palette in the drawn pill shape
until design extends the set.FlightNotificationText.Classify
reading the alert's sentence — the service sends the same title
("Flight Changed") on every alert, so the title cannot make a list scannable. One
sentence routinely reports several changes, so the classifier detects all of
them (ChangeSignals) and then picks one to show, in this order:
CANCELLED — the flight is gone; nothing else matters.DELAYED — changes the member's plans; a gate named in the same
breath does not.GATE CHANGETIMES UPDATED / ARRIVAL UPDATED /
DEPARTURE UPDATEDif statements in one method, which is not a rule anyone can
review — and it was wrong: gate sat above delayed, so a live alert reading
"now departs from gate T12, departs at 21:29, arrives at 23:36, arrives at gate
E53, has a status of Delayed" displayed GATE CHANGE and the delay
never reached the card (QA, 2026-08-19). Two readings of the same words are kept
apart deliberately: "arrives at gate E53" is a gate change, "arrives at
23:36" is an arrival time.
flight-alert.svg triangle for
DELAYED, not a distinct "several changes" glyph. There is no such glyph
in the drawn set, and inventing one would put a second, competing severity signal on
a card whose sentence already lists every change.psH738AqHDxuMyFm897f9r ·
Component: notification card flight status (5054:13801) in Notifications screen (5060:14989) ·
captured 2026-06-26
| State | Description |
|---|---|
| Collapsed (default) | Caret chevron points down — indicates content can be expanded. Content area shows route + date. |
| Expanded | Captured 2026-08-12 as the open variant (5054:13777): a status row above the caret pill — flight number ("DL 302", Futura Medium 16px) left, severity pill right (radius 24, 2px/8px padding, 12px navy text; yellow/500 for "Delayed 34m"), separated from the content block by a 1px gray/100 (#f3f6f7) top border. The variant's caret is drawn unrotated; the implementation flips it while open for affordance. |
Captured from Notifications — Comms tab screen (parent page node notifications-comms
5060:15039, instances 5056:13946/13947). Same component name
(notification card flight status), same container structure, different slot content.
Implemented 2026-08-12 as CommsNotificationCard.razor, sharing the
alpa-fsnc-* CSS block with the flight variant — one component in the design, one CSS
block in code (the old alpa-notif-card row styles were removed). The drawn expander
pill is empty in this variant and is rendered as drawn (decorative); the whole card remains the
tap target, routed by the host through NotificationContentRouter — behavior the
design does not address. Weblink rows open the app's auto-login AuthenticatedWebViewPage
modal (renamed from WebViewPage, AB#2591) via IDocumentActionNavigator.NavigateToWebLinkAsync — the same path
native's notification list takes — not the system browser (parity fix 2026-08-12: Safari bounced
member-only links to a second login). The same card serves the live Comms tab and the read-state scenarios
surface (/notifications/comms-scenarios), which inherits the redesign
automatically.
| Slot | Flight variant | Comms variant |
|---|---|---|
| Timestamp | Inter Regular 14px, right-aligned, secondary color — always elapsed time ("7min ago"), never a calendar date; the implementation's ladder continues upward ("2 hrs ago" · "3 days ago" · "2 wks ago" · "3 mos ago" · "1 yr ago") in RelativeTimeExtensions. Note: both feeds send offset-naive timestamps (verified against production 2026-08-12) — the elapsed value assumes they are local wall clock; an explicit-offset ask is flagged for the backend. | Same ("2 hrs ago" drawn) |
| Icon circle | 44×44px circle, 18×18 inner image (airline/status icon) | Same structure (generic icon placeholder) |
| Line 1 (primary) | Route string — e.g. "DEN » MIA" — Futura PT Demi 20px, nowrap | Category/source — e.g. "Operations Bulletin", "From the President" — Futura PT Demi 20px, same font |
| Line 2 (secondary) | Date string — e.g. "Fri April 24" — Afacad SemiBold 18px (body/m), 1 line | Article headline/body — Afacad SemiBold (body/m), multi-line, leading-[1.3] |
| Expand button | 18px pill with 12px caret icon | 18px pill drawn empty (no caret) — rendered as drawn, decorative; the comms card does not expand, its tap opens the routed destination |
The comms variant body text font size shows a fallback of 20px in the Figma export vs 18px for the flight variant, both using var(--font-size/body/m). Treat as the same token — confirm the token resolves to 18px with the design system.
Tier: domain control. Spec-only ViewModel — not added to ComponentViewModels.cs until implementation.
| Screen | Node | Tab | Role |
|---|---|---|---|
| Notifications | 5054:13801 | Flight Finder tab | Repeating list item — one per flight status notification |
| Notifications (Comms tab) | 5056:13946 / 5056:13947 | Comms tab | Comms variant — same component, different slot content (category + headline instead of route + date). See § Comms variant above. |
border-radius:38px) with only a caret icon (no text). Do not add a text label to this button.icon-generic in Figma). The inner image is 18×18px. The icon source is flight-status-specific — may be an airline logo or status icon from the notification payload.») separator. This is a text character, not an SVG. Use the Unicode right double angle quotation mark: U+00BB.open variant (5054:13777) in the working file — see the Expanded row in the variants table. The design draws only the status row; everything else the implementation shows when expanded is implementation-defined (see the implementation callout).FlightNotificationCard.razor and was aligned to the delivered design 2026-08-12
(see the implementation callout at the top).Target home: ALPAMobile.HybridUi. Component file: NotificationCardFlightStatus.razor.
| Parameter | Type | Required | Description |
|---|---|---|---|
Timestamp | string | Yes | Human-readable elapsed time (e.g. "7min ago"). Formatted by the ViewModel, not the component. |
IconUrl | string? | No | URL for 18×18 icon inside the 44px circle. Null renders a default placeholder circle. |
Route | string | Yes | Route string (e.g. "DEN » MIA"). Include the » separator in the data. |
Date | string | Yes | Formatted date string (e.g. "Fri April 24"). |
IsExpanded | bool | No | Controls caret direction. Default: false. Expanded detail content is a child RenderFragment. |
ExpandedContent | RenderFragment? | No | Detail content shown when IsExpanded is true. Allows callers to provide domain-specific expanded view. |
OnExpandToggle | EventCallback | No | Fires when caret button is tapped. Consumer toggles IsExpanded on the ViewModel. |
@* Notifications page — Flight Finder tab *@
@foreach (var notif in Vm.FlightNotifications)
{
<AlpaNotificationCardFlightStatus
Timestamp="@notif.Timestamp"
IconUrl="@notif.IconUrl"
Route="@notif.Route"
Date="@notif.Date"
IsExpanded="@notif.IsExpanded"
OnExpandToggle="@(() => notif.ToggleExpand())" />
}
Classes will live in ALPAMobile.HybridUi/wwwroot/css/alpa-components.css under the alpa-notif-card-fs BEM block ("fs" = flight status).
| Class | Scope | Key rules |
|---|---|---|
.alpa-notif-card-fs | Root card | background:white; border-bottom:1px solid var(--border-default); display:flex; flex-direction:column; gap:8px; padding-top:8px; width:100%; cursor:pointer |
.alpa-notif-card-fs__header | Timestamp + content area | display:flex; flex-direction:column; gap:0 |
.alpa-notif-card-fs__timestamp-row | Right-aligned timestamp row | display:flex; justify-content:flex-end; padding:0 16px |
.alpa-notif-card-fs__timestamp | Time elapsed text | font-family:var(--font-family/caption); font-weight:var(--font-weight/caption); font-size:14px; color:var(--text-secondary); line-height:1.5; white-space:nowrap |
.alpa-notif-card-fs__content-row | Icon + text row | display:flex; align-items:center; gap:16px; padding:0 16px |
.alpa-notif-card-fs__icon | 44px circle icon slot | width:44px; height:44px; border-radius:50%; display:flex; align-items:center; justify-content:center; overflow:hidden; flex-shrink:0 |
.alpa-notif-card-fs__icon img | Icon image | width:18px; height:18px |
.alpa-notif-card-fs__text | Route + date text column | display:flex; flex-direction:column; flex:1; min-width:0 |
.alpa-notif-card-fs__route | Route line (DEN » MIA) | font-family:var(--font-family/body); font-weight:500; font-size:20px; color:var(--text-primary); line-height:1.3; white-space:nowrap |
.alpa-notif-card-fs__date | Date line | font-family:var(--font-family/alt); font-weight:600; font-size:18px; color:var(--text-primary); line-height:1.2 |
.alpa-notif-card-fs__expand-btn | Caret-only expand button | background:white; border:none; border-radius:38px; width:100%; height:18px; display:flex; align-items:center; justify-content:center; padding:4px 12px; cursor:pointer |
.alpa-notif-card-fs__caret | Caret icon wrapper | width:12px; height:12px; display:flex; align-items:center; justify-content:center · SVG rotates 180° when IsExpanded via alpa-notif-card-fs__caret--up |
.alpa-notif-card-fs__caret--up | Expanded caret modifier | transform:rotate(180deg) |