Skip to content
Signal

Platforms

SwiftUI mapping

How each token and component maps to SwiftUI.

PieceImplementation
ColoursAsset catalog colour sets with Any / Dark appearances, named after the tokens
Line coloursCTA hue tokens with contrast variants: Yellow darkens in light, Brown/Purple lighten in dark
TypeFont.custom("Figtree", size:, relativeTo:) wrapped in Font.ct(.display | .title | .body | .sub | .cap)
RouteIconViewCircle or capsule at .mini / .standard / .large
StationIconViewHStack(spacing: 0) in a Capsule filled card2 for every station: CTIconView(icon: .train) at 75% in a square frame of the height, then HStack(spacing: 1.5) of RoundedRectangle(cornerRadius: 0.09h) per line at 0.225h × 0.6h with a −0.08h leading offset; trailing padding max(4, 0.22h)
VehicleMarkerViewZStack in a 44 frame, hit testing off: a separate VehicleArrowShape offset 20.5 and rotated to the bearing, then the body (train: route-colour Circle; bus: RoundedRectangle(cornerRadius: 7, style: .continuous) in Brand) with a 2pt Paper keyline (Ink when selected; a bus adds a 1 Paper hairline inside) and an upright glyph. Stale fills the body Muted
LineBarViewHStack(spacing: 3) of fixed 10 × 24 active line-colour RoundedRectangles(cornerRadius: 3); no border, no count-based resizing
Line orderPure function of the set of line IDs; compute once and cache
MotionOne sheet spring (DesignTokens): UISpringTimingParameters(duration: 0.42, bounce: 0.15), damping 0.85 for FloatingPanel. Reduce Motion: 0.17 s critically damped, or a 0.17 s fade for the station slide
Hit areas.frame(minWidth: 44, minHeight: 44).contentShape(Rectangle()) on every Button and map Annotation
RouteFilterSingle-select binding holding All or one route ID; selected state uses the shared selection halo
Detail overlaysThe stop detail is its own FloatingPanel added above the page sheet. It slides up at .half while the page sheet moves to .hidden on the same spring; dismissing reverses both and restores the stored page and MapSheetDetent. Search is a page of the same sheet at .full
Core sheetOne FloatingPanel over the persistent Map: .tip is 180pt from the safe-area bottom, .half is fractional 0.5, and .full is 4pt from the safe-area top; initial .half; 6pt leading/trailing/bottom container margins; 28pt continuous corner radius; backdrop alpha 0; removal disabled. MapFloatingPanelBehavior rejects momentum projection across anchors; redirectional progress allows only a drag that physically crosses half to continue. Rubber-banding and scroll tracking remain on. CoreSheetView constrains content to the visible detent and the system tab bar floats over it
CameraMapView owns camera moves: rider gestures, MapUserLocationButton, the Nearby low-to-up transition, opening a stop (centre above the half sheet, 1,400 m minimum zoom, .smooth 0.45 s), the title-tap re-pan (CenterStopOnMapAction environment value) and the arrival-tap pan to a vehicle. Sheet detent changes never assign camera position
Navigation and controlsMainView owns one persistent MapView. MapPanelModel holds the Home or Nearby base page plus Search and stop overlay state. CoreTabBar is a system TabView (Home, Nearby, and Search in the search role) whose empty tabs only choose the page. CoreSheetView renders a person.crop.circle profile button at the top trailing corner. Search route roots use NavigationStack inside the overlay. The Map keeps system .mapControls; searchable, Form, Toggle, Picker, toolbar buttons, alerts and confirmation dialogs remain native.
IconsSystem controls use SF Symbols; transit presentation uses CTIconView, with RoundedStarShape for Favorite. Train, Bus and Train + bus are template-rendered asset catalog images synced from icons/ with the same names