Skip to content

Commit f4af5f8

Browse files
committed
frontend/app: Add ARIA semantics to app shell and navigation (Phase 12 P0)
1 parent 0fc1c9e commit f4af5f8

File tree

4 files changed

+48
-2
lines changed

4 files changed

+48
-2
lines changed

src/packages/frontend/app/connection-indicator.tsx

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,19 @@ export const ConnectionIndicator: React.FC<Props> = React.memo(
6868
: {}),
6969
} as const;
7070

71+
function getConnectionLabel() {
72+
switch (connection_status) {
73+
case "connected":
74+
return intl.formatMessage(labels.connected);
75+
case "connecting":
76+
return intl.formatMessage(labels.connecting);
77+
case "disconnected":
78+
return intl.formatMessage(labels.disconnected);
79+
default:
80+
return "Connection status unknown";
81+
}
82+
}
83+
7184
function render_connection_status() {
7285
if (connection_status === "connected") {
7386
return (
@@ -103,12 +116,22 @@ export const ConnectionIndicator: React.FC<Props> = React.memo(
103116
return (
104117
<div
105118
className={TOP_BAR_ELEMENT_CLASS}
119+
role="status"
120+
aria-label={getConnectionLabel()}
121+
aria-live="polite"
122+
aria-busy={connection_status === "connecting"}
106123
style={outer_style}
107124
onClick={connection_click}
125+
onKeyDown={(e) => {
126+
if (e.key === "Enter" || e.key === " ") {
127+
e.preventDefault();
128+
connection_click();
129+
}
130+
}}
131+
tabIndex={0}
108132
>
109133
{render_connection_status()}
110134
</div>
111135
);
112136
},
113137
);
114-

src/packages/frontend/app/nav-tab.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ interface Props {
2727
on_click?: () => void;
2828
style?: CSS;
2929
tooltip?: string;
30+
// ARIA attributes for tab semantics
31+
role?: string;
32+
"aria-label"?: string;
3033
}
3134

3235
export const NavTab: React.FC<Props> = React.memo((props: Props) => {
@@ -140,6 +143,15 @@ export const NavTab: React.FC<Props> = React.memo((props: Props) => {
140143
return (
141144
<div
142145
onClick={onClick}
146+
onKeyDown={(e) => {
147+
if (e.key === "Enter" || e.key === " ") {
148+
e.preventDefault();
149+
onClick();
150+
}
151+
}}
152+
role={props.role ?? "button"}
153+
aria-label={props["aria-label"]}
154+
tabIndex={0}
143155
style={outer_style}
144156
className={TOP_BAR_ELEMENT_CLASS}
145157
>

src/packages/frontend/app/page.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import BalanceButton from "@cocalc/frontend/purchases/balance-button";
3636
import PayAsYouGoModal from "@cocalc/frontend/purchases/pay-as-you-go/modal";
3737
import openSupportTab from "@cocalc/frontend/support/open";
3838
import { webapp_client } from "@cocalc/frontend/webapp-client";
39-
import { COLORS } from "@cocalc/util/theme";
39+
import { COLORS, SITE_NAME } from "@cocalc/util/theme";
4040
import { IS_IOS, IS_MOBILE, IS_SAFARI } from "../feature";
4141
import { ActiveContent } from "./active-content";
4242
import { ConnectionIndicator } from "./connection-indicator";
@@ -124,6 +124,7 @@ export const Page: React.FC = () => {
124124

125125
const is_commercial = useTypedRedux("customize", "is_commercial");
126126
const insecure_test_mode = useTypedRedux("customize", "insecure_test_mode");
127+
const site_name = useTypedRedux("customize", "site_name") ?? SITE_NAME;
127128

128129
function account_tab_icon(): IconName | React.JSX.Element {
129130
if (is_anonymous) {
@@ -289,6 +290,8 @@ export const Page: React.FC = () => {
289290
return (
290291
<div
291292
className="smc-right-tabs-fixed"
293+
role="region"
294+
aria-label="Top navigation controls"
292295
style={{
293296
display: "flex",
294297
flex: "0 0 auto",
@@ -362,6 +365,8 @@ export const Page: React.FC = () => {
362365
// ARIA: main element serves as the primary landmark for the entire application
363366
const body = (
364367
<main
368+
role="main"
369+
aria-label={`${site_name} application`}
365370
style={PAGE_STYLE}
366371
onDragOver={(e) => e.preventDefault()}
367372
onDrop={drop}

src/packages/frontend/i18n/common.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -725,6 +725,12 @@ export const labels = defineMessages({
725725
description:
726726
"Short label, telling the user a possible connection has not been established.",
727727
},
728+
connected: {
729+
id: "labels.connected",
730+
defaultMessage: "Connected",
731+
description:
732+
"Short label, telling the user the connection has been established.",
733+
},
728734
connection: {
729735
id: "labels.connection",
730736
defaultMessage: "Connection",

0 commit comments

Comments
 (0)