mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-26 06:08:40 +01:00
a11y: dashboard and navigation semantics (navbar, cards, sync, switchers) (#3468)
* Label new-UI dashboard navigation and sync status for screen readers Each bottom-nav tab becomes one button node carrying its localized name, its selected state and mutual exclusion; the pill text no longer duplicates the selected tab. The assets/history tabs gain selected state, the Bitcoin/Lightning switcher becomes a labeled toggle, and the sync bar becomes a single button whose label is the localized status (plus Tor/MWEB/Silent Payments badges) with a hint pointing at node management. Compact mode's pulsing dot and the chain icon's progress ring now have text equivalents, and the invisible pointer-absorbing strip behind the nav bar is excluded from traversal. * Make new-UI dashboard cards, lists and menus operable by screen readers Collapse the wallet-name row into one labeled button (hardware-wallet glyph folded into the label, inner accounts button no longer a second stop) and give balance cards a labeled, selectable node with a long-press action for show/hide balance. Balances hidden behind AnimatedOpacity, card artwork and decorative glyphs leave the semantics tree; the 3-dots customize target and the card action buttons are now named buttons. Assets/history rows, the history header, the MWEB promo, long-press menu items, the account cards and the add-account and generate-name controls become single button nodes, the phantom action chip in the assets header is built conditionally instead of drawn at opacity 0, and app cards get a close tooltip plus an 'opens externally' hint. * Use expression bodies for the new a11y helpers * Restore untouched expressions in cards_view * Address review: state-aware balance hint, tooltip-free close label * Address review: keep header height without the chip, name the selected index
This commit is contained in:
@@ -187,50 +187,56 @@ class _NEWNewMainNavBarState extends State<NewMainNavBar> {
|
||||
? iconHorizontalPadding / 100
|
||||
: 0),
|
||||
curve: Curves.easeOutCubic,
|
||||
child: InkWell(
|
||||
splashFactory: NoSplash.splashFactory,
|
||||
splashColor: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(pillBorderRadius),
|
||||
onTap: () => _onItemTap(i),
|
||||
child: AnimatedContainer(
|
||||
duration:
|
||||
_firstFrame ? Duration.zero : inactiveIconMoveDuration,
|
||||
curve: Curves.easeOutCubic,
|
||||
width: i == widget.selectedIndex ? pillWidth : iconBoxWidth,
|
||||
alignment: Alignment.center,
|
||||
child: AnimatedAlign(
|
||||
duration: inactiveIconFadeDuration,
|
||||
child: Semantics(
|
||||
button: true,
|
||||
selected: i == widget.selectedIndex,
|
||||
inMutuallyExclusiveGroup: true,
|
||||
label: visibleActions[i].name(context),
|
||||
child: InkWell(
|
||||
splashFactory: NoSplash.splashFactory,
|
||||
splashColor: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(pillBorderRadius),
|
||||
onTap: () => _onItemTap(i),
|
||||
child: AnimatedContainer(
|
||||
duration:
|
||||
_firstFrame ? Duration.zero : inactiveIconMoveDuration,
|
||||
curve: Curves.easeOutCubic,
|
||||
width: i == widget.selectedIndex ? pillWidth : iconBoxWidth,
|
||||
alignment: Alignment.center,
|
||||
child: AnimatedScale(
|
||||
duration: inactiveIconAppearDuration,
|
||||
child: AnimatedAlign(
|
||||
duration: inactiveIconFadeDuration,
|
||||
curve: Curves.easeOutCubic,
|
||||
scale: (i == widget.selectedIndex) ? 0.857 : 1.0,
|
||||
child: TweenAnimationBuilder<Color?>(
|
||||
tween: ColorTween(
|
||||
begin: (i == widget.selectedIndex)
|
||||
? inactiveColor
|
||||
: activeColor,
|
||||
end: (i == widget.selectedIndex)
|
||||
? activeColor
|
||||
: inactiveColor,
|
||||
),
|
||||
duration: iconColorChangeDuration,
|
||||
builder: (context, value, child) {
|
||||
return Container(
|
||||
height: NewMainNavBar.barHeight,
|
||||
child: CakeImageWidget(
|
||||
imageUrl: visibleActions[i].image,
|
||||
width: iconWidth,
|
||||
height: iconHeight,
|
||||
//fit: BoxFit.scaleDown,
|
||||
colorFilter: ColorFilter.mode(
|
||||
value ?? inactiveColor,
|
||||
BlendMode.srcIn,
|
||||
alignment: Alignment.center,
|
||||
child: AnimatedScale(
|
||||
duration: inactiveIconAppearDuration,
|
||||
curve: Curves.easeOutCubic,
|
||||
scale: (i == widget.selectedIndex) ? 0.857 : 1.0,
|
||||
child: TweenAnimationBuilder<Color?>(
|
||||
tween: ColorTween(
|
||||
begin: (i == widget.selectedIndex)
|
||||
? inactiveColor
|
||||
: activeColor,
|
||||
end: (i == widget.selectedIndex)
|
||||
? activeColor
|
||||
: inactiveColor,
|
||||
),
|
||||
duration: iconColorChangeDuration,
|
||||
builder: (context, value, child) {
|
||||
return Container(
|
||||
height: NewMainNavBar.barHeight,
|
||||
child: CakeImageWidget(
|
||||
imageUrl: visibleActions[i].image,
|
||||
width: iconWidth,
|
||||
height: iconHeight,
|
||||
//fit: BoxFit.scaleDown,
|
||||
colorFilter: ColorFilter.mode(
|
||||
value ?? inactiveColor,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -306,11 +312,15 @@ class AnimatedPill extends StatelessWidget {
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: pillIconWidth + 2),
|
||||
child: Text(
|
||||
currentAction.name(context),
|
||||
style: pillTextStyle.copyWith(color: contentColor),
|
||||
overflow: TextOverflow.fade,
|
||||
softWrap: false,
|
||||
// The selected tab's InkWell already announces this name, so the
|
||||
// pill text must not become a second stop for screen readers.
|
||||
child: ExcludeSemantics(
|
||||
child: Text(
|
||||
currentAction.name(context),
|
||||
style: pillTextStyle.copyWith(color: contentColor),
|
||||
overflow: TextOverflow.fade,
|
||||
softWrap: false,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user