Feed tabs say what they are

Below xl every feed tab but the selected one was a lone icon with no name, for a screen reader or a finger. DTab takes
an AccessibleName, which DTabs renders as the tab's label and tooltip, and FeedTabs names each tab; on a phone every
tab shows its name too. The strip is marked as one that scrolls sideways (data-scroll-x), says it holds the feeds, and
on a phone fades at the edge where more tabs wait (edge-fade-x, a scroll-driven animation where the browser has one);
the selected tab is brought into view when it changes (window.revealActive). Explore's tabs get the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
This commit is contained in:
thepraandClaude Opus 5.5 committed 2026-10-06 23:31:07 +02:00
1 parent 1c9773bab0
commit dd38002682
9 files changed
+97 -6

No files matched your search

+2
View File
@@ -9,6 +9,8 @@
[Parameter] public RenderFragment ChildContent { get; set; }
[Parameter] public bool Disabled { get; set; }
[Parameter] public string Class { get; set; }
/// <summary>The tab's name for a screen reader and as its tooltip, when its Header shows only an icon.</summary>
[Parameter] public string AccessibleName { get; set; }
protected override void OnInitialized()
{
+1
View File
@@ -12,6 +12,7 @@
var index = i;
var tab = tabs[i];
<button type="button" role="tab" aria-selected="@(index == Active ? "true" : "false")" disabled="@tab.Disabled"
aria-label="@tab.AccessibleName" title="@tab.AccessibleName"
class="@Cx("tab", index == Active ? "tab-active" : null, tab.Disabled ? "tab-disabled" : null, tab.Class)"
@onclick="() => Select(index)">
@tab.Header
+18 -4
View File
@@ -6,14 +6,17 @@
read; the buttons on the right open the composer and, on narrow screens, the globe. *@
<div class="flex flex-col gap-2">
<div class="flex items-center gap-2 min-w-0">
<div class="flex-1 min-w-0 overflow-x-auto">
<DTabs Active="ActiveIndex" ActiveChanged="Select" Variant="DTabsVariant.Box" Size="DSize.Sm" ContentClass="hidden" class="flex-nowrap w-max">
@* a sideways strip on a phone, every tab named, the selected one brought into view and the edges faded where more
tabs wait; from md only the selected tab shows its name until xl *@
<div @ref="strip" class="flex-1 min-w-0 overflow-x-auto max-md:edge-fade-x" data-scroll-x>
<DTabs Active="ActiveIndex" ActiveChanged="Select" Variant="DTabsVariant.Box" Size="DSize.Sm" ContentClass="hidden" class="flex-nowrap w-max"
aria-label="@Localizer["Feeds"]">
@foreach (var type in Types)
{
<DTab Class="gap-1 whitespace-nowrap">
<DTab Class="gap-1 whitespace-nowrap" AccessibleName="@TabName(type)">
<Header>
<i class="@type.GetTimelineTypeIcon()" aria-hidden="true"></i>
<span class="@(type == Filter.TimelineType ? null : "hidden xl:inline")">@TabName(type)</span>
<span class="@(type == Filter.TimelineType ? null : "hidden max-md:inline xl:inline")">@TabName(type)</span>
@if (type is TimelineType.Notifications && UnreadNotifications > 0)
{
<DBadge Size="DSize.Xs" Color="DColor.Primary" title="@Localizer["{0} unread", UnreadNotifications]" data-unread>@UnreadNotifications</DBadge>
@@ -127,6 +130,17 @@
int ActiveIndex => Types.IndexOf(Filter.TimelineType);
ElementReference strip;
int revealed = -1;
protected override void OnAfterRender(bool firstRender)
{
if (ActiveIndex == revealed)
return;
revealed = ActiveIndex;
CascadingState.Status.RevealActive(strip);
}
ViewGroup SelectedGroup => Groups.FirstOrDefault(g => g.Id == Filter.GroupId);
[Inject] DiscoveryService DiscoveryService { get; set; }