TopBar
The screen header — an inset rounded card with the status bar, a three-column header row, an optional large title and add-ons such as search, chips and tabs.
topbarOpen in Figma ↗Screen-level header assembly. Used everywhere by default. Combines the left side (account or back button), a middle part (page name, logo or title), controls, and optional add-ons — chips, inputs, tabs or stepper.
Use for: the header of every screen. Exactly one per screen.
Not for: section headings within a page — use section or page-header.
Choosing: white on light screens, midnight on dark, red for brand moments, grey for utility screens.
Rules: fixed to the top, outside any section. Add-ons stack below the main bar; enable only what the screen needs.
Inverse variants come by default for level-1 screens. The midnight version is selected only when specified by the design. Default scheme variants are used for LV2 and futher types of pages and must use 'false or 'white', depending on initial canvas color to reach contrast. If canvas color is white 'default' - use 'white' surface. If it's shades of midnight light greys - use 'false'
import SwiftUI import EandTokens // V1.2 header: a 4pt-inset rounded card (36 top / 20 bottom) on brand, midnight, white or // none. The middle (logo or page name) is always centred; back + 40pt glass links either // side. Big-title block padded 16/16/0, then the add-on slot (16pt rhythm). struct TopBar<AddOns: View>: View { var onBack: (() -> Void)? @ViewBuilder var addOns: AddOns var body: some View { VStack(alignment: .leading, spacing: 0) { ZStack { EtisalatLogo(scheme: .white, size: 40) // lockup; red on white / none HStack { if let onBack { TopBarLink(icon: "chevron.left", action: onBack) } Spacer() TopBarLink(icon: "wand.and.stars") {} TopBarLink(icon: "bell") {} } } .padding(.horizontal, EandSpacing.lg) VStack(alignment: .leading, spacing: 0) { Text("Overline").font(.system(size: EandTypography.bodyMd.size)) Text("Large title goes here") // heading/lg style: 24 / 110% .font(.system(size: EandTypography.headingLg.size, weight: EandTypography.headingLg.weight)) Text("Lorem ipsum dolor sit amet.").font(.system(size: EandTypography.bodyLg.size)) .padding(.top, EandSpacing.sm) } .padding([.top, .horizontal], EandSpacing.lg) VStack(spacing: EandSpacing.lg) { addOns } // searchbar, glass chips, tabs .padding([.top, .horizontal], EandSpacing.lg) } .foregroundColor(EandColor.textDefaultInverse) .padding(.bottom, EandSpacing.lg) .background(EandColor.surfaceBaseBrand) // 36 has no radius token yet (design question 14); the bottom binds radius7 = 20. .clipShape(UnevenRoundedRectangle(topLeadingRadius: 36, bottomLeadingRadius: EandRadius.radius7, bottomTrailingRadius: EandRadius.radius7, topTrailingRadius: 36)) .padding([.top, .horizontal], EandSpacing.xs) } } // topbar-link on brand / midnight: glass inverse, white 20% + blur, icon white 75%. struct TopBarLink: View { let icon: String; var action: () -> Void var body: some View { Button(action: action) { Image(systemName: icon).font(.system(size: EandIcon.lg)) .foregroundColor(EandColor.sectionLinkGlassInverseIconDefault) .frame(width: EandScale.scale40, height: EandScale.scale40) .background(EandColor.sectionLinkGlassInverseSurfaceDefault, in: Circle()) } } }
When to use
- Use once per screen, at the top. Home and hub screens take
surface="brand"with anaccountor abigTitle. Detail and pushed pages takeonBackand atitle, onwhiteover a white canvas ornoneover a grey or midnight one. - Don't use for headings inside the page (use PageHeader below the bar, or Section), or for the title of a sheet (use BottomSheet).
Choosing a surface
surface | Fill | Header scheme |
|---|---|---|
brand | surface/base/brand | White ink, glass controls, white logo |
midnight | surface/base/midnight | White ink, glass controls, white logo |
white | surface/base/inverse | Midnight ink, grey controls, red logo |
none | Transparent | Midnight ink, white controls, red logo |
Without a surface, the bar is brand when it has an account or bigTitle, and white
otherwise. A title-only <TopBar title="…" /> is the Figma page header: a white card with the
status bar and a centred page name.
Anatomy
- Status bar — a 62px faux iOS bar, on by default.
statusBar={false}removes it. - Header row — three columns; the middle stays centred whatever the sides hold.
- Left:
onBackrenders the 40px back button;leadingtakes custom content;accountshows an optional 40pxavatar, agreetinginbody/mdand the masked number intitle/smwith a 16px chevron (chevron={false}hides it). - Middle:
logo(truerenders the etisalat lockup, white or red to suit the surface), or atitleintitle/smwith an optionalsubtitleinbody/md. - Right:
actions, each wrapped in a 40px round control (pass icons), thentrailing.
- Left:
- Title block —
eyebrowinbody/md(eyebrowChevronadds a 16px chevron),bigTitlein bold 24px (chevronadds a 24px chevron after it), andsubtextinbody/lg. - Add-ons —
children, such as a Searchbar, Chip row, Tabs or Stepper, each with 16px above and at the sides.
<TopBar
surface="brand"
account={{ greeting: 'Good morning, Sara', name: '050 123 4567' }}
logo
actions={[<Icon key="alerts" name="notification" size={20} />]}
/>
<TopBar surface="white" onBack={() => history.back()} title="Usage" subtitle="050 123 4567" />Deprecated
surface="default"is nowsurface="white", andvariantis nowsurface.greetingis nowaccount={{ greeting }}.actionBaris replaced by a card passed aschildren.compactrenders a code-only 56px row with no status bar. Figma has no compact top bar, so use the default header withonBackandtitleinstead.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
account? | TopBarAccount | — | Left column: account — optional avatar, greeting over a masked number with a chevron. |
actionBar? | TopBarAction | — | Deprecated. V1.0 sub-card — pass a card via children instead. |
actions? | ReactNode[] | — | Right column: 40px round controls (Figma topbar-link). Pass icons. |
bigTitle? | ReactNode | — | Title block: the large title (heading/lg text style, 24px). |
chevron? | boolean | — | Chevron after the big title (24px, default off) and the account number (16px, default on). |
children? | ReactNode | — | Add-ons: search, tabs, chips, stepper, action cards… each gets 16px top padding. |
compact? | boolean | — | Deprecated. Code-only 56px single-row header with no status bar. Figma has no compact top bar — every V1.2 surface carries the status bar and header row. |
eyebrow? | ReactNode | — | Title block (.topbar-title): overline above the large title, body/md. |
eyebrowChevron? | boolean | — | Title block: 16px chevron after the overline (Figma overline-chevron). |
greeting? | ReactNode | — | Deprecated. Use account={{ greeting }}. Renders the account display. |
leading? | ReactNode | — | Left column: custom content (e.g. an avatar). For a back button use onBack. |
logo? | ReactNode | — | Middle: the etisalat logo (true) or a custom node. |
onBack? | (() => void) | — | Left column: renders the 40px back button (Figma .topbar-body-left-side type=back-button). |
rounded? | boolean | — | Figma rounds every surface (36 top / 20 bottom). false squares the corners. |
statusBar? | boolean | — | Faux iOS status bar (on by default on every surface). |
subtext? | ReactNode | — | Title block: supporting line under the title, body/lg. |
subtitle? | ReactNode | — | Middle: body/md line under the page name (Figma description-string). |
surface? | 'default' | 'brand' | 'midnight' | 'none' | 'white' | — | Figma surface: brand (brand-red), midnight, white or none (Figma false,
transparent — for a midnight or light-grey canvas). Defaults to brand when an
account or bigTitle is given, else white. default is a deprecated alias of white. |
title? | ReactNode | — | Middle: page name, centred title/sm (Figma .topbar-page-name). Ignored when logo is set. |
trailing? | ReactNode | — | Right column: a link or button after the controls. |
variant? | 'default' | 'brand' | 'midnight' | 'none' | 'white' | — | Deprecated. Use surface. |
Design
Figma source: topbar — etisalat Consumer App DSL V1.2
- Every surface is an inset card: 4px around the top and sides (
spacing/xs), a 36px top radius and a 20px bottom radius (border-radius/7), 16px bottom padding. The 36px radius has no token, so it is a literal.rounded={false}squares all four corners. - The status bar is 62px tall with 15/24/19/24 padding, the values of the iOS kit Figma uses.
- Header row: 40px minimum (
scale/40), 16px side padding (spacing/lg), on aminmax(0, 1fr) auto minmax(0, 1fr)grid. - Controls follow the header scheme: 20% white glass with a blur on
brandandmidnight, solid #f0f0f5 onwhite, solid white onnone. - The large title applies the
heading/lgtext style: 24px, 110%, snug tracking, with 16/16/0 padding around the block.