Skip to content

Commit 20eef8a

Browse files
authored
[Label] Expose SectionHeader labels as semantic headings (#887)
1 parent e9c893b commit 20eef8a

5 files changed

Lines changed: 89 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## [60.2.6]
2+
- [Label] `SectionHeader` labels are now exposed as level 2 semantic headings by default, improving VoiceOver and TalkBack navigation for section headers.
3+
14
## [60.2.5]
25
- [Pickers][Android] Added error haptic feedback when TimePicker or DateAndTimePicker selections are clamped by minimum or maximum constraints.
36

src/library/DIPS.Mobile.UI/Resources/Styles/Label/LabelStyleResources.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,22 @@ internal class LabelStyleResources
2323
[LabelStyle.Header700] = s_sharedStyles[TextStyle.Header700],
2424
[LabelStyle.Header600] = s_sharedStyles[TextStyle.Header600],
2525
[LabelStyle.Header500] = s_sharedStyles[TextStyle.Header500],
26-
[LabelStyle.SectionHeader] = s_sharedStyles[TextStyle.SectionHeader]
26+
[LabelStyle.SectionHeader] = CreateSectionHeaderStyle()
2727
};
28+
29+
private static Style CreateSectionHeaderStyle()
30+
{
31+
return new Style(typeof(Components.Labels.Label))
32+
{
33+
BasedOn = s_sharedStyles[TextStyle.SectionHeader],
34+
Setters =
35+
{
36+
new Setter
37+
{
38+
Property = SemanticProperties.HeadingLevelProperty,
39+
Value = SemanticHeadingLevel.Level2
40+
}
41+
}
42+
};
43+
}
2844
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using System.Linq;
2+
using DIPS.Mobile.UI.Resources.Styles;
3+
using DIPS.Mobile.UI.Resources.Styles.Label;
4+
using DIPS.Mobile.UI.Resources.Styles.Span;
5+
using Microsoft.Maui;
6+
using Microsoft.Maui.Controls;
7+
using DuiStyles = DIPS.Mobile.UI.Resources.Styles.Styles;
8+
9+
namespace DIPS.Mobile.UI.UnitTests.Resources.Styles;
10+
11+
public class SectionHeaderStyleTests
12+
{
13+
[Fact]
14+
public void LabelSectionHeaderStyle_ShouldSetHeadingLevel2()
15+
{
16+
var style = DuiStyles.GetLabelStyle(LabelStyle.SectionHeader);
17+
18+
var headingLevelSetter = GetSetters(style).Single(setter => setter.Property == SemanticProperties.HeadingLevelProperty);
19+
20+
headingLevelSetter.Value.Should().Be(SemanticHeadingLevel.Level2);
21+
}
22+
23+
[Fact]
24+
public void SpanSectionHeaderStyle_ShouldNotSetHeadingLevel()
25+
{
26+
var style = DuiStyles.GetSpanStyle(SpanStyle.SectionHeader);
27+
28+
GetSetters(style).Should().NotContain(setter => setter.Property == SemanticProperties.HeadingLevelProperty);
29+
}
30+
31+
private static IEnumerable<Setter> GetSetters(Style? style)
32+
{
33+
while (style is not null)
34+
{
35+
foreach (var setter in style.Setters.OfType<Setter>())
36+
{
37+
yield return setter;
38+
}
39+
40+
style = style.BasedOn;
41+
}
42+
}
43+
}

wiki/Components/Labels.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,19 @@ Each of these style categories has a different weight, such as: `UI400`, to cont
1515
1616
Inspect [LabelStyle.cs](https://github.com/DIPSAS/DIPS.Mobile.UI/blob/main/src/library/DIPS.Mobile.UI/Resources/Styles/Label/LabelStyle.cs) to see all of the different styles.
1717

18+
## SectionHeader and accessibility
19+
20+
Use `LabelStyle.SectionHeader` for labels that visually introduce a section, group, or list of content. A `dui:Label` with this style is exposed as a semantic heading with `SemanticProperties.HeadingLevel=Level2` by default, so screen reader users can navigate to it as a heading.
21+
22+
```xml
23+
<dui:Label Text="Recent patients"
24+
Style="{dui:Styles Label=SectionHeader}" />
25+
```
26+
27+
Override `SemanticProperties.HeadingLevel` on the label when the information hierarchy needs a different level, for example `Level3` for a subsection or `None` when the label is not actually a heading.
28+
29+
For formatted text headings, apply `LabelStyle.SectionHeader` to the parent `dui:Label`. `SpanStyle.SectionHeader` is visual text styling only and does not make the label a semantic heading by itself.
30+
1831
# Usage
1932
Here we place a `Label` with a style of `Body` with a weight of `400`.
2033

wiki/Interaction & Accessibility/Accessibility.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,19 @@ Available heading levels: `None`, `Level1`, `Level2`, `Level3`, `Level4`, `Level
166166

167167
**Note:** Not all platforms support all heading levels. Check the specific platform documentation for details.
168168

169+
#### Section headers
170+
171+
Use `dui:Label` with `LabelStyle.SectionHeader` for text that visually introduces a section, group, or list. This style sets `SemanticProperties.HeadingLevel=Level2` by default on labels, giving the visual section header matching screen reader heading semantics.
172+
173+
```xml
174+
<dui:Label Text="Recent patients"
175+
Style="{dui:Styles Label=SectionHeader}" />
176+
```
177+
178+
Set `SemanticProperties.HeadingLevel` explicitly when a different level is needed, such as `Level3` for a subsection, or `None` when the label uses the visual style without representing a real heading.
179+
180+
When using `FormattedString`, put the `SectionHeader` label style and heading level on the parent `dui:Label`. Span styles are visual only and do not expose heading semantics by themselves.
181+
169182
---
170183

171184
## MAUI AutomationProperties

0 commit comments

Comments
 (0)