Interface patterns give you an opportunity to explore different interface designs. Be sure to check out How to Adapt a Pattern for Your Application.
This page explains how you can use this pattern in your interface, and walks through the design structure in detail.
There are three options for navigation patterns which provide a way to structure a group of pages with an icon/text-based left navigation. When an icon/title is selected, the corresponding page is displayed. All three have similar functionality and design. These patterns are located under the PATTERNS tab on the left navigation menu of the palette. Test out all three to see which option matches your business needs. Variations of the same set of data are configured similarly in each navigation pattern.
When adapting one of these patterns, be sure to employ best UX design practices, like adding a descriptive caption to the icons and using accessibilityText
to help screen reader users know when icons or cards are selected.
Let's take a closer look at the "Navigation (Collapsible)" pattern.
The main components in this pattern are a Column which toggles between "EXTRA-NARROW"
and "NARROW"
widths, Card Layouts with links, and a set of Styled Icons and titles.
This pattern introduces a 203-line expression to the interface.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
{
a!localVariables(
/* Defines whether or not to collapse the side nav */
local!collapseNav: false,
/* The selected navigation section */
local!activeCollapsibleNavSection: 1,
/* The navigation sections */
local!collapsibleNavSections: {
a!map(name: "Workspace", icon: "briefcase"),
a!map(name: "Tasks", icon: "tasks"),
a!map(name: "Requests", icon: "paper-plane"),
a!map(name: "Calendar", icon: "calendar"),
a!map(name: "My Time", icon: "clock-o"),
a!map(name: "Expenses", icon: "money")
},
{
a!columnsLayout(
columns: {
a!columnLayout(
contents: {
a!forEach(
items: local!collapsibleNavSections,
expression: if(
local!collapseNav,
{
a!richTextDisplayField(
value: {
a!richTextIcon(
icon: fv!item.icon,
caption: fv!item.name,
link: a!dynamicLink(
value: fv!index,
saveInto: local!activeCollapsibleNavSection
),
linkStyle: "STANDALONE",
color: if(
fv!index = local!activeCollapsibleNavSection,
"ACCENT",
"SECONDARY"
),
size: "LARGE"
)
},
align: "CENTER",
accessibilityText: if(
fv!index = local!activeCollapsibleNavSection,
fv!item.name & " " & "selected",
""
)
)
},
a!cardLayout(
contents: a!sideBySideLayout(
items: {
a!sideBySideItem(
item: a!richTextDisplayField(
labelPosition: "COLLAPSED",
value: a!richTextIcon(
icon: fv!item.icon,
color: "SECONDARY",
size: if(local!collapseNav, "LARGE", "MEDIUM")
),
align: "LEFT"
),
width: "MINIMIZE"
),
a!sideBySideItem(
item: a!richTextDisplayField(
labelPosition: "COLLAPSED",
value: a!richTextItem(
text: fv!item.name,
color: "ACCENT",
size: "MEDIUM",
style: if(
fv!index = local!activeCollapsibleNavSection,
"STRONG",
"PLAIN"
)
)
),
showWhen: not(local!collapseNav)
)
},
alignVertical: "MIDDLE"
),
link: a!dynamicLink(
value: fv!index,
saveInto: local!activeCollapsibleNavSection
),
style: if(
fv!index = local!activeCollapsibleNavSection,
"ACCENT",
"NONE"
),
showBorder: false,
accessibilityText: if(
fv!index = local!activeCollapsibleNavSection,
fv!item.name & " " & "selected",
""
)
)
)
),
a!richTextDisplayField(
value: a!richTextIcon(
icon: if(
local!collapseNav,
"angle-double-right",
"angle-double-left"
),
caption: if(
local!collapseNav,
"Expand navigation bar",
"Collapse navigation bar"
),
link: a!dynamicLink(
value: not(local!collapseNav),
saveInto: local!collapseNav
),
linkStyle: "STANDALONE",
color: "SECONDARY",
size: "MEDIUM"
),
align: "CENTER"
),
/* This card is used to set a minimum height on the column so that the *
* divider takes up more screen space when there is minimal content. *
* Once content is added to the main column, this can be removed. */
a!cardLayout(
height: "TALL",
showWhen: not(a!isNativePhone()),
showBorder: false
)
},
width: if(local!collapseNav, "EXTRA_NARROW", "NARROW")
),
a!columnLayout(
contents: {
/* Conditionally display selected navigation section. *
* Sections are created individually here because they will *
* have varying contents, so if you change the list in *
* local!collapisbleNavSections, you will need to make sure *
* the list of sections here is the correct length. */
choose(
local!activeCollapsibleNavSection,
a!sectionLayout(
label: index(
local!collapsibleNavSections.name,
local!activeCollapsibleNavSection,
""
),
contents: {}
),
a!sectionLayout(
label: index(
local!collapsibleNavSections.name,
local!activeCollapsibleNavSection,
""
),
contents: {}
),
a!sectionLayout(
label: index(
local!collapsibleNavSections.name,
local!activeCollapsibleNavSection,
""
),
contents: {}
),
a!sectionLayout(
label: index(
local!collapsibleNavSections.name,
local!activeCollapsibleNavSection,
""
),
contents: {}
),
a!sectionLayout(
label: index(
local!collapsibleNavSections.name,
local!activeCollapsibleNavSection,
""
),
contents: {}
),
a!sectionLayout(
label: index(
local!collapsibleNavSections.name,
local!activeCollapsibleNavSection,
""
),
contents: {}
)
)
}
)
},
spacing: "SPARSE",
showDividers: true
)
}
)
}
At the beginning of the expression, three local variables are set up: local!collapseNav
, which stores whether or not the side navigation is collapsed, local!activeCollapsibleNavSection
, which stores the currently selection, and local!collapsibleNavSections
, which stores the list of page names and their respective icons. local!activeCollapsibleNavSection
is initialized to the first page by default.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
a!localVariables(
/* Defines whether or not to collapse the side nav */
local!collapseNav: false,
/* The selected navigation section */
local!activeCollapsibleNavSection: 1,
/* The navigation sections */
local!collapsibleNavSections: {
a!map(name: "Workspace", icon: "briefcase"),
a!map(name: "Tasks", icon: "tasks"),
a!map(name: "Requests", icon: "paper-plane"),
a!map(name: "Calendar", icon: "calendar"),
a!map(name: "My Time", icon: "clock-o"),
a!map(name: "Expenses", icon: "money")
},
The left navigation is constructed two different ways depending on whether or not it is collapsed. When expanded, the left navigation is built using Card Layouts with Dynamic Links and Rich Text inside a NARROW
Column. When collapsed, the left navigation is built using LARGE
Styled Icons with Dynamic Links inside an EXTRA-NARROW
Column.
Inside of the first column, we iterate over each section defined in local!collapsibleNavSections
using a!forEach
. This is where we then determine what size navigation to show by checking the value of local!collapseNav
. The components used for each size vary, but both use fv!index
to save the index of the selected section to local!activeCollapsibleNavSection
in the dynamic link.
Notice that for the Card Layouts, we set showBorder
to false
for a better user experience.
We use a Styled Icon in combination with a Dynamic Link to toggle the width of the left navigation at the bottom of the navigation.
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
{
a!columnsLayout(
columns: {
a!columnLayout(
contents: {
a!forEach(
items: local!collapsibleNavSections,
expression: if(
local!collapseNav,
{
a!richTextDisplayField(
value: {
a!richTextIcon(
icon: fv!item.icon,
caption: fv!item.name,
link: a!dynamicLink(
value: fv!index,
saveInto: local!activeCollapsibleNavSection
),
linkStyle: "STANDALONE",
color: if(
fv!index = local!activeCollapsibleNavSection,
"ACCENT",
"SECONDARY"
),
size: "LARGE"
)
},
align: "CENTER",
accessibilityText: if(
fv!index = local!activeCollapsibleNavSection,
fv!item.name & " " & "selected",
""
)
)
},
a!cardLayout(
contents: a!sideBySideLayout(
items: {
a!sideBySideItem(
item: a!richTextDisplayField(
labelPosition: "COLLAPSED",
value: a!richTextIcon(
icon: fv!item.icon,
color: "SECONDARY",
size: if(local!collapseNav, "LARGE", "MEDIUM")
),
align: "LEFT"
),
width: "MINIMIZE"
),
a!sideBySideItem(
item: a!richTextDisplayField(
labelPosition: "COLLAPSED",
value: a!richTextItem(
text: fv!item.name,
color: "ACCENT",
size: "MEDIUM",
style: if(
fv!index = local!activeCollapsibleNavSection,
"STRONG",
"PLAIN"
)
)
),
showWhen: not(local!collapseNav)
)
},
alignVertical: "MIDDLE"
),
link: a!dynamicLink(
value: fv!index,
saveInto: local!activeCollapsibleNavSection
),
style: if(
fv!index = local!activeCollapsibleNavSection,
"ACCENT",
"NONE"
),
showBorder: false,
accessibilityText: if(
fv!index = local!activeCollapsibleNavSection,
fv!item.name & " " & "selected",
""
)
)
)
),
a!richTextDisplayField(
value: a!richTextIcon(
icon: if(
local!collapseNav,
"angle-double-right",
"angle-double-left"
),
caption: if(
local!collapseNav,
"Expand navigation bar",
"Collapse navigation bar"
),
link: a!dynamicLink(
value: not(local!collapseNav),
saveInto: local!collapseNav
),
linkStyle: "STANDALONE",
color: "SECONDARY",
size: "MEDIUM"
),
align: "CENTER"
),
/* This card is used to set a minimum height on the column so that the *
* divider takes up more screen space when there is minimal content. *
* Once content is added to the main column, this can be removed. */
a!cardLayout(
height: "TALL",
showWhen: not(a!isNativePhone()),
showBorder: false
)
},
width: if(local!collapseNav, "EXTRA_NARROW", "NARROW")
),
Once the user clicks on an icon or a card in the left navigation pane, the selected section will render in the right column. In this pattern, we have only configured a basic Section Layout to display. You will want to add your components to the contents of the section or replace the section with an Interface Object for the associated section.
We also take advantage of the showDividers
parameter in our Columns Layout to create a better division between our navigation and section contents.
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
a!columnLayout(
contents: {
/* Conditionally display selected navigation section. *
* Sections are created individually here because they will *
* have varying contents, so if you change the list in *
* local!collapisbleNavSections, you will need to make sure *
* the list of sections here is the correct length. */
choose(
local!activeCollapsibleNavSection,
a!sectionLayout(
label: index(
local!collapsibleNavSections.name,
local!activeCollapsibleNavSection,
""
),
contents: {}
),
a!sectionLayout(
label: index(
local!collapsibleNavSections.name,
local!activeCollapsibleNavSection,
""
),
contents: {}
),
a!sectionLayout(
label: index(
local!collapsibleNavSections.name,
local!activeCollapsibleNavSection,
""
),
contents: {}
),
a!sectionLayout(
label: index(
local!collapsibleNavSections.name,
local!activeCollapsibleNavSection,
""
),
contents: {}
),
a!sectionLayout(
label: index(
local!collapsibleNavSections.name,
local!activeCollapsibleNavSection,
""
),
contents: {}
),
a!sectionLayout(
label: index(
local!collapsibleNavSections.name,
local!activeCollapsibleNavSection,
""
),
contents: {}
)
)
}
)
},
spacing: "SPARSE",
showDividers: true
)
}
)
}
In addition to the "Navigation (Collapsible)" pattern described above, there are two more navigation pattern options in the Interface Designer: "Navigation" and "Navigation (Subsections)".
This pattern is a simplified version of the "Navigation (Collapsible)" pattern outlined above. In this pattern, only a "NARROW"
Column with Card Layouts is used.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
{
a!localVariables(
/* The selected navigation section */
local!activeNavSection: 1,
/* The navigation sections */
local!navSections: {
a!map(name: "Workspace", icon: "briefcase"),
a!map(name: "Tasks", icon: "tasks"),
a!map(name: "Requests", icon: "paper-plane"),
a!map(name: "Calendar", icon: "calendar"),
a!map(name: "My Time", icon: "clock-o"),
a!map(name: "Expenses", icon: "money")
},
{
a!columnsLayout(
columns: {
a!columnLayout(
contents: {
a!forEach(
items: local!navSections,
expression: a!cardLayout(
contents: a!sideBySideLayout(
items: {
a!sideBySideItem(
item: a!richTextDisplayField(
labelPosition: "COLLAPSED",
value: a!richTextIcon(
icon: fv!item.icon,
color: "SECONDARY",
size: "MEDIUM"
),
align: "LEFT"
),
width: "MINIMIZE"
),
a!sideBySideItem(
item: a!richTextDisplayField(
labelPosition: "COLLAPSED",
value: a!richTextItem(
text: fv!item.name,
color: "ACCENT",
size: "MEDIUM",
style: if(
fv!index = local!activeNavSection,
"STRONG",
"PLAIN"
)
)
)
)
},
alignVertical: "MIDDLE"
),
link: a!dynamicLink(
value: fv!index,
saveInto: local!activeNavSection
),
style: if(
fv!index = local!activeNavSection,
"ACCENT",
"NONE"
),
showBorder: false,
accessibilityText: if(
fv!index = local!activeNavSection,
fv!item.name & " " & "selected",
""
)
)
),
/* This card is used to set a minimum height on the column so that the *
* divider takes up more screen space when there is minimal content. *
* Once content is added to the main column, this can be removed. */
a!cardLayout(
height: "TALL",
showWhen: not(a!isNativePhone()),
showBorder: false
)
},
width: "NARROW"
),
a!columnLayout(
contents: {
/* Conditionally display selected navigation section. *
* Sections are created individually here because they will *
* have varying contents, so if you change the list in *
* local!navSections, you will need to make sure *
* the list of sections here is the correct length. */
choose(
local!activeNavSection,
a!sectionLayout(
label: index(
local!navSections.name,
local!activeNavSection,
""
),
contents: {}
),
a!sectionLayout(
label: index(
local!navSections.name,
local!activeNavSection,
""
),
contents: {}
),
a!sectionLayout(
label: index(
local!navSections.name,
local!activeNavSection,
""
),
contents: {}
),
a!sectionLayout(
label: index(
local!navSections.name,
local!activeNavSection,
""
),
contents: {}
),
a!sectionLayout(
label: index(
local!navSections.name,
local!activeNavSection,
""
),
contents: {}
),
a!sectionLayout(
label: index(
local!navSections.name,
local!activeNavSection,
""
),
contents: {}
)
)
}
)
},
spacing: "SPARSE",
showDividers: true
)
}
)
}
This pattern is a variation of the "Navigation" pattern. It follows a very similar style, but allows you to group section options to help with organization.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
{
a!localVariables(
/* The current navigation section */
local!activeSubNavSection: 1,
/* The selected navigation subsection */
local!activeSubNavSubsection: 1,
/* The navigation sections */
local!subNavSections: {
a!map(
sectionName: "Quick Links",
subsections: {
"Workspace",
"Tasks",
"Requests",
"Calendar"
}
),
a!map(
sectionName: "Favorites",
subsections: {
"My Time",
"Expenses"
}
)
},
{
a!columnsLayout(
columns: {
a!columnLayout(
contents: {
a!forEach(
items: local!subNavSections,
expression: a!localVariables(
local!parentSection: fv!index,
{
a!richTextDisplayField(
labelPosition: if(fv!isFirst, "COLLAPSED", "ABOVE"),
value: {
a!richTextItem(
text: fv!item.sectionName,
size: "MEDIUM",
style: "STRONG"
)
}
),
a!forEach(
items: fv!item.subsections,
expression: a!cardLayout(
contents: a!richTextDisplayField(
labelPosition: "COLLAPSED",
value: {
a!richTextItem(
text: {
a!richTextItem(
text: fv!item,
color: "ACCENT",
size: "STANDARD",
style: if(
and(
local!parentSection = local!activeSubNavSection,
fv!index = local!activeSubNavSubsection
),
"STRONG",
"PLAIN"
)
)
}
)
}
),
link: a!dynamicLink(
saveInto: {
a!save(local!activeSubNavSubsection, fv!index),
a!save(local!activeSubNavSection, local!parentSection)
}
),
style: if(
and(
local!parentSection = local!activeSubNavSection,
fv!index = local!activeSubNavSubsection
),
"ACCENT",
"NONE"
),
showBorder: false,
accessibilityText: if(
and(
local!parentSection = local!activeSubNavSection,
fv!index = local!activeSubNavSubsection
),
fv!item & " " & "selected",
""
)
)
)
}
)
),
/* This card is used to set a minimum height on the column so that the *
* divider takes up more screen space when there is minimal content. *
* Once content is added to the main column, this can be removed. */
a!cardLayout(
height: "TALL",
showWhen: not(a!isNativePhone()),
showBorder: false
)
},
width: "NARROW"
),
a!columnLayout(
contents: {
/* Conditionally display selected navigation section. *
* Sections are created individually here because they will *
* have varying contents, so if you change the list in *
* local!subNavSections, you will need to make sure *
* the list of sections here is the correct length. */
choose(
local!activeSubNavSection,
choose(
local!activeSubNavSubsection,
a!sectionLayout(
label: index(
index(
local!subNavSections.subsections,
local!activeSubNavSection,
""
),
local!activeSubNavSubsection,
""
),
contents: {}
),
a!sectionLayout(
label: index(
index(
local!subNavSections.subsections,
local!activeSubNavSection,
""
),
local!activeSubNavSubsection,
""
),
contents: {}
),
a!sectionLayout(
label: index(
index(
local!subNavSections.subsections,
local!activeSubNavSection,
""
),
local!activeSubNavSubsection,
""
),
contents: {}
),
a!sectionLayout(
label: index(
index(
local!subNavSections.subsections,
local!activeSubNavSection,
""
),
local!activeSubNavSubsection,
""
),
contents: {}
)
),
choose(
local!activeSubNavSubsection,
a!sectionLayout(
label: index(
index(
local!subNavSections.subsections,
local!activeSubNavSection,
""
),
local!activeSubNavSubsection,
""
),
contents: {}
),
a!sectionLayout(
label: index(
index(
local!subNavSections.subsections,
local!activeSubNavSection,
""
),
local!activeSubNavSubsection,
""
),
contents: {}
)
)
)
}
)
},
spacing: "SPARSE",
showDividers: true
)
}
)
}