Skip to content

Commit 87e319a

Browse files
nunoeufrasioclaude
andauthored
style(lens-switcher): visual polish on sidebar left rail (#691)
* style(lens-switcher): visual polish on sidebar left rail - active lens: solid blue-500 bg + white icon and label - inactive items: opacity 75 (was 70) - separators: opacity 35 (was 20) - what's new icon: bullhorn (was clock-rotate-left) - changelog badge: plain 6px accent dot instead of numbered badge Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Nuno Eufrasio <nmeufrasio@gmail.com> * fix(lens-switcher): address PR #691 review feedback Address review comments from copilot-pull-request-reviewer[bot], coderabbitai[bot]: - lens-switcher.component.html: add aria-hidden="true" to the dot badge span so screen readers ignore it (per copilot-pull-request-reviewer[bot]) - lens-switcher.component.html: bind aria-label dynamically to include unseen count when > 0, e.g. "What's New (3 unseen updates)" (per copilot-pull-request-reviewer[bot]) - lens-switcher.component.ts: imports array formatting — Prettier resolves to single-line per project config; coderabbitai[bot] suggestion was a false positive (no change needed) Resolves 2 review threads. Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Nuno Eufrasio <nmeufrasio@gmail.com> * fix(lens-switcher): address PR #691 review feedback (iteration 2) Address review comments from @MRashad26: - lens-switcher.component.ts: add changelogAriaLabel computed signal with correct singular/plural ("1 update" vs "N updates") (per @MRashad26) - lens-switcher.component.html: simplify aria-label binding to use the new computed signal — removes escaped apostrophes and double signal eval (per @MRashad26) - lens-switcher.component.html: remove stray whitespace text node from the dot badge span (per @MRashad26) - lens-switcher.component.html: add @let isActive inside @for loop so activeLens() === lens.id is evaluated once per item, not 3× (per @MRashad26) WCAG AA contrast comment (inactive text-blue-200/75) intentionally not addressed — design decision to keep current opacity. Resolves 3 of 4 review threads. Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Nuno Eufrasio <nmeufrasio@gmail.com> --------- Signed-off-by: Nuno Eufrasio <nmeufrasio@gmail.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 5923662 commit 87e319a

2 files changed

Lines changed: 20 additions & 20 deletions

File tree

apps/lfx-one/src/app/shared/components/lens-switcher/lens-switcher.component.html

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,12 @@
6060
</button>
6161

6262
<!-- Divider -->
63-
<hr class="w-6 border-white/20 mb-3" />
63+
<hr class="w-6 border-white/35 mb-3" />
6464

6565
<!-- Lens Buttons -->
6666
<div class="flex flex-col items-center gap-3 flex-1">
6767
@for (lens of lenses(); track lens.id) {
68+
@let isActive = activeLens() === lens.id;
6869
<button
6970
type="button"
7071
(click)="setLens(lens.id)"
@@ -73,14 +74,14 @@
7374
[pTooltip]="lens.label"
7475
tooltipPosition="right"
7576
class="group flex flex-col items-center gap-1 w-full transition-colors"
76-
[ngClass]="activeLens() === lens.id ? 'text-blue-400' : 'text-blue-200/70 hover:text-white'">
77+
[ngClass]="isActive ? 'text-white' : 'text-blue-200/75 hover:text-white'">
7778
<div
7879
class="flex items-center justify-center w-10 h-10 rounded-xl transition-colors"
7980
[ngClass]="{
80-
'bg-blue-500/20': activeLens() === lens.id,
81-
'group-hover:bg-white/10': activeLens() !== lens.id,
81+
'bg-blue-500': isActive,
82+
'group-hover:bg-white/10': !isActive,
8283
}">
83-
<i [ngClass]="activeLens() === lens.id ? lens.activeIcon : lens.icon" class="text-xl"></i>
84+
<i [ngClass]="isActive ? lens.activeIcon : lens.icon" class="text-xl"></i>
8485
</div>
8586
<span class="text-[10px] font-medium leading-none">{{ lens.shortLabel }}</span>
8687
</button>
@@ -102,19 +103,14 @@
102103
type="button"
103104
(click)="openChangelogDrawer()"
104105
data-testid="lens-changelog-button"
105-
aria-label="What's New"
106+
[attr.aria-label]="changelogAriaLabel()"
106107
pTooltip="What's New"
107108
tooltipPosition="right"
108-
class="group flex flex-col items-center gap-1 w-full transition-colors text-blue-200/70 hover:text-white">
109+
class="group flex flex-col items-center gap-1 w-full transition-colors text-blue-200/75 hover:text-white">
109110
<div class="relative flex items-center justify-center w-10 h-10 rounded-xl transition-colors group-hover:bg-white/10">
110-
<i class="fa-light fa-clock-rotate-left text-xl"></i>
111+
<i class="fa-light fa-bullhorn text-xl"></i>
111112
@if (unseenChangelogCount() > 0) {
112-
<lfx-badge
113-
[value]="unseenChangelogCount()"
114-
severity="danger"
115-
size="small"
116-
styleClass="!absolute -top-1 -right-1"
117-
data-testid="lens-changelog-badge" />
113+
<span aria-hidden="true" class="absolute -top-1 -right-1 w-1.5 h-1.5 rounded-full bg-blue-500" data-testid="lens-changelog-badge"></span>
118114
}
119115
</div>
120116
</button>
@@ -128,7 +124,7 @@
128124
aria-label="LFX Insights"
129125
[pTooltip]="insightsTooltip"
130126
tooltipPosition="right"
131-
class="group flex flex-col items-center gap-1 w-full transition-colors text-blue-200/70 hover:text-white">
127+
class="group flex flex-col items-center gap-1 w-full transition-colors text-blue-200/75 hover:text-white">
132128
<div class="flex items-center justify-center w-10 h-10 rounded-xl transition-colors group-hover:bg-white/10">
133129
<i class="fa-light fa-chart-simple text-xl"></i>
134130
</div>
@@ -143,15 +139,15 @@
143139
aria-label="Impersonate user"
144140
pTooltip="Impersonate User"
145141
tooltipPosition="right"
146-
class="group flex flex-col items-center gap-1 w-full transition-colors text-blue-200/70 hover:text-white">
142+
class="group flex flex-col items-center gap-1 w-full transition-colors text-blue-200/75 hover:text-white">
147143
<div class="flex items-center justify-center w-10 h-10 rounded-xl transition-colors group-hover:bg-white/10">
148144
<i class="fa-light fa-user-secret text-xl"></i>
149145
</div>
150146
</button>
151147
}
152148

153149
<!-- Divider -->
154-
<hr class="w-6 border-white/20" />
150+
<hr class="w-6 border-white/35" />
155151

156152
<!-- User Avatar -->
157153
<button

apps/lfx-one/src/app/shared/components/lens-switcher/lens-switcher.component.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@
22
// SPDX-License-Identifier: MIT
33

44
import { NgClass } from '@angular/common';
5-
import { afterNextRender, Component, inject, input, signal, viewChild } from '@angular/core';
5+
import { afterNextRender, Component, computed, inject, input, signal, viewChild } from '@angular/core';
66
import { Router, RouterLink } from '@angular/router';
77
import { AvatarComponent } from '@components/avatar/avatar.component';
8-
import { BadgeComponent } from '@components/badge/badge.component';
98
import { ButtonComponent } from '@components/button/button.component';
109
import { ChangelogDrawerComponent } from '@components/changelog-drawer/changelog-drawer.component';
1110
import { ImpersonationDialogComponent } from '@components/impersonation-dialog/impersonation-dialog.component';
@@ -21,7 +20,7 @@ import { TooltipModule } from 'primeng/tooltip';
2120

2221
@Component({
2322
selector: 'lfx-lens-switcher',
24-
imports: [NgClass, RouterLink, TooltipModule, PopoverModule, AvatarComponent, BadgeComponent, ButtonComponent, ChangelogDrawerComponent],
23+
imports: [NgClass, RouterLink, TooltipModule, PopoverModule, AvatarComponent, ButtonComponent, ChangelogDrawerComponent],
2524
providers: [DialogService],
2625
templateUrl: './lens-switcher.component.html',
2726
styleUrl: './lens-switcher.component.scss',
@@ -46,6 +45,11 @@ export class LensSwitcherComponent {
4645
protected readonly isImpersonating = this.userService.impersonating;
4746
protected readonly unseenChangelogCount = this.changelogService.unseenChangelogCount;
4847
protected readonly changelogDrawerVisible = signal(false);
48+
protected readonly changelogAriaLabel = computed(() => {
49+
const count = this.unseenChangelogCount();
50+
if (count === 0) return "What's New";
51+
return `What's New (${count} unseen ${count === 1 ? 'update' : 'updates'})`;
52+
});
4953

5054
public constructor() {
5155
// afterNextRender so input bindings have settled — the duplicate `[mobile]="true"` instance correctly skips.

0 commit comments

Comments
 (0)