Section
The body container — a rounded surface with a header and a body slot sized to the card type it holds.
sectionOpen in Figma ↗Content block wrapper. Optional header (title, description, filter pill, trigger) plus a body slot holding cards or rows.
USE FOR Every content block on a page. All content components sit inside a section.
NOT FOR Fixed chrome. Topbar, navbar and tab-pill are placed directly on the screen.
SIZE
Content determines rows; rows determine size. Never resize the body manually.
xs 1 row (72) · sm 2 (148) · md 3 (224) · lg 4 (300) · xl 6 (452)
Count the rows the content needs, then pick the matching size. If the content does not fill the size, step down one. Never exceed xl.
single input field → xs or sm full highlight banner → xl
COLOUR
Set on the section, never on the content inside it.
inverse=false → default · raised · ai-powered
inverse=true → brand · midnight-base · midnight-raised · glass-midnight · glass-white
Use glass only over imagery. It has no contrast guarantee on flat colour.
Use gradient only to emphasise the most important section, and only on pages with more than two sections.
CONTENT
One major item per section, or several repeating components such as a set of forms.
A major component plus a supporting one is allowed — a stack of cards with a cta-bar below, for example — provided the total stays within xl.
GRID
Two systems. The general grid applies to the whole screen. The section grid applies inside the body slot only.
Section grid: up to 6 rows, each with 24 responsive columns and constant 4px spacing.
Columns stretch to fill the row at any container or screen width. Spacing is always 4px unless a placed component adds its own internal padding.
RULES
One section per content block.
Never nest sections.
Never stretch beyond xl.
import SwiftUI import EandTokens // V1.2 section: radius 24, header padded 16/16/0 with a 40pt field row — 24pt title // (heading/lg style) and the trigger (40pt section-link chevron, or a tertiary lg button). // Body padded 16; its min height reserves the row for the card type it holds. struct SectionView<Content: View>: View { let title: String; let context: String var bodyHeight: CGFloat = EandSection.bodyHeightRow3 // row-1 72 · 2 148 · 3 224 · 4 300 · 6 452 @ViewBuilder var content: Content var body: some View { VStack(alignment: .leading, spacing: 0) { VStack(alignment: .leading, spacing: EandSpacing.xs) { HStack(spacing: 0) { Text(title) .font(.system(size: EandTypography.headingLg.size, weight: EandTypography.headingLg.weight)) .lineLimit(1) Spacer(minLength: EandSpacing.md) SectionLinkButton(action: .forward) // 40pt, r14, solid white } .frame(minHeight: EandScale.scale40) Text(context).font(.system(size: EandTypography.bodyMd.size)) .foregroundColor(EandColor.textDefaultSubtle) } .padding([.top, .horizontal], EandSpacing.lg) content .frame(maxWidth: .infinity, minHeight: bodyHeight, alignment: .topLeading) .padding(EandSpacing.lg) } .foregroundColor(EandColor.textDefaultDefault) .background(EandColor.surfaceBaseDefault) .clipShape(RoundedRectangle(cornerRadius: EandRadius.radius8)) } }
When to use
- Use for every content block in a screen body: one section per group of cards, rows or tiles, with a title that says what the group is.
- Don't use to open a page; that is PageHeader, below the TopBar. Don't nest sections. For a page-end block of questions, use FAQ on the page canvas rather than styling questions inside a section.
Anatomy
-
Header —
title(24px bold, one or two lines throughtitleLine), an optionalfilterPillplaced inline right after the title, and atriggerat the row end.contextadds abody/mdline under the row.showHeader={false}removes the header entirely. -
Trigger —
chevron(default) is the 40px section-link button;buttonis a tertiarylgButton labelledtriggerLabel(default "See all");nonehides it. Both callonTrigger. -
Body slot —
childrensit in a 16px-padded slot.sizereserves the row height the card type needs:sizeSlot height Fits xs72px list rows, action bars sm148px quick-action and service tiles md224px deal, new-on and general cards lg300px plan and product cards xl452px highlight banners -
carouselmakes the body scroll sideways and bleed to the section edges.gradientadds the sunset glow behind the body; reserve it for the most important section on the screen.
Choosing a surface
surface | Fill | Ink |
|---|---|---|
default | surface/base/default (#f0f0f5) | default |
raised | surface/raised/default (#fff) | default |
ai-powered | Light red-to-grey gradient | default |
brand | surface/base/brand | inverse |
midnight-base | surface/base/midnight | inverse |
midnight-raised | surface/raised/midnight | inverse |
glass-midnight | surface/glass/midnight/md | inverse |
glass-white | surface/glass/white/lg | inverse |
Use the glass surfaces only over imagery. On the inverse surfaces, pass inverse to the filter
pill yourself; the title, context and trigger switch automatically.
<Section
title="Plans for you"
surface="midnight-base"
filterPill={<FilterPill inverse>Postpaid</FilterPill>}
trigger="button"
triggerLabel="Manage"
size="lg"
carousel
>
<PlanCard colorScheme="inverse" />
<PlanCard colorScheme="inverse" />
</Section>Deprecated
hideChevron is now trigger="none", and onSeeAll is now onTrigger. The V1.1 surface
names still work: inverse renders brand and midnight renders midnight-base.
brand-muted is code-only and has no V1.2 counterpart.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
carousel? | boolean | — | Body scrolls horizontally and bleeds to the edges (carousels). |
context? | ReactNode | — | Subtext under the title (body.md, subtle). |
filterPill? | ReactNode | — | Optional filter pill, inline after the title (Figma filter-pill). Pass inverse on dark surfaces. |
gradient? | boolean | — | Sunset glow over the body (Figma .section-body gradient). Only on the most important section. |
hideChevron? | boolean | — | Deprecated. Use trigger="none". |
onSeeAll? | (() => void) | — | Deprecated. Use onTrigger. |
onTrigger? | (() => void) | — | |
showHeader? | boolean | true | Hide the whole header (Figma show-header). |
size? | 'lg' | 'md' | 'sm' | 'xs' | 'xl' | — | Body slot size — reserves the row height for the card type it holds. |
surface? | 'default' | 'inverse' | 'brand' | 'midnight' | 'raised' | 'ai-powered' | 'midnight-base' | 'midnight-raised' | 'glass-midnight' | 'glass-white' | 'brand-muted' | default | |
title? | ReactNode | Section | |
titleLine? | 1 | 2 | 1 | Clamp the title to one or two lines (Figma lines). |
trigger? | 'none' | 'button' | 'chevron' | — | Header trigger: the section-link chevron, a tertiary lg button, or none (Figma .section-trigger link | button). |
triggerLabel? | ReactNode | See all | Label for the button trigger. |
Design
Figma source: section — etisalat Consumer App DSL V1.2
- 24px radius (
border-radius/8). Header padding 16/16/0 (spacing/lg); a 40px field row (scale/40); the filter pill sits 8px after the title (spacing/sm); the trigger has 12px of inline-start padding (spacing/md). - The title applies the
heading/lgtext style: 24px (font/size/7), 110%, snug tracking. - Body padding 16. Slot heights bind
section/body/height/row-N: 72, 148, 224, 300 and 452. - The sunset glow is two layer-blurred ellipses, orange #fa700c and brand red #e00800 at 27%
(25% on
xsandsm), with the layer at 96%. Figma leaves these values unbound, so the code uses them as literals. - The
ai-poweredgradient (#ffe4e3 to #f5f5f7) is also unbound. The glass surfaces carry no blur, because the published surface symbols have none.