Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions frontend/sync.js
Original file line number Diff line number Diff line change
Expand Up @@ -375,11 +375,13 @@ export function setupSyncFeature() {
eyeOffIcon.style.display = 'block';
togglePasswordBtn.setAttribute('aria-label', '隱藏密碼');
togglePasswordBtn.setAttribute('title', '隱藏密碼');
togglePasswordBtn.setAttribute('aria-pressed', 'true');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similar to the theme toggle, the password visibility button uses a dynamic label ("隱藏密碼" / "顯示密碼"). Per ARIA standards, aria-pressed should not be used on buttons whose labels change to reflect their state. It is recommended to remove the aria-pressed attribute to avoid redundant or confusing information for screen reader users.

References
  1. According to WAI-ARIA guidelines, toggle buttons should not use aria-pressed if their label changes dynamically to reflect the state.

} else {
eyeIcon.style.display = 'block';
eyeOffIcon.style.display = 'none';
togglePasswordBtn.setAttribute('aria-label', '顯示密碼');
togglePasswordBtn.setAttribute('title', '顯示密碼');
togglePasswordBtn.setAttribute('aria-pressed', 'false');
}
});
}
Expand Down
1 change: 1 addition & 0 deletions frontend/theme.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export function applyTheme(theme) {
const nextThemeLabel = theme === 'light' ? '深色' : '淺色';
toggleBtn.setAttribute('aria-label', `切換至${nextThemeLabel}模式`);
toggleBtn.setAttribute('title', `切換至${nextThemeLabel}模式`);
toggleBtn.setAttribute('aria-pressed', theme === 'dark' ? 'true' : 'false');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

According to the WAI-ARIA Authoring Practices Guide (APG), a button that changes its label to reflect its state (e.g., "切換至深色模式" vs "切換至淺色模式") is not considered a toggle button and must not use the aria-pressed attribute. The label change itself is sufficient to communicate the state change. Adding aria-pressed to a button with a dynamic label can lead to confusing announcements in screen readers, such as "Switch to Light Mode, toggle button, pressed".

References
  1. According to WAI-ARIA guidelines, toggle buttons should not use aria-pressed if their label changes dynamically to reflect the state.

}

document.dispatchEvent(new CustomEvent('themechange', {
Expand Down
8 changes: 4 additions & 4 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
</noscript>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit" async defer></script>
<script src="/theme-init.js"></script>
<link rel="stylesheet" href="/dist/main-bt8E1vXG.css" id="vite-css">
<link rel="stylesheet" href="/dist/main-D2sAJccc.css" id="vite-css">

Copilot AI Apr 9, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These /dist/main-*.css and /dist/main-*.js hash updates look unrelated to the stated purpose of adding aria-pressed. If this PR isn’t intended to include a full asset rebuild, consider reverting these hash-only changes (or updating the PR description) to avoid unnecessary merge conflicts/churn.

Copilot uses AI. Check for mistakes.
<link rel="icon" type="image/x-icon" href="/favicon.ico?v=20260402">
<link rel="icon" type="image/png" href="/favicon-96x96.png?v=20260402" sizes="96x96" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg?v=20260402" />
Expand Down Expand Up @@ -91,7 +91,7 @@ <h1>成績分析平台</h1>
</div>
<div class="flex-center-gap-8 header-actions">
<button class="icon-btn theme-toggle-btn" id="themeToggleBtn" type="button" aria-label="切換至淺色模式"
title="切換主題">
title="切換主題" aria-pressed="false">
Comment on lines 93 to +94

Copilot AI Apr 9, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aria-pressed is initialized to false in the static HTML, but the app’s default theme is dark (no data-theme unless grades-theme is 'light'). That means the initial ARIA state is incorrect for the default path until JS runs. Consider setting the initial aria-pressed to match the default rendered theme (and letting applyTheme() keep it in sync thereafter).

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

As this button uses a dynamic label to communicate state changes, the aria-pressed attribute should be removed to comply with ARIA best practices for toggle buttons.

Suggested change
title="切換主題" aria-pressed="false">
title="切換主題">

<span class="dropdown-icon theme-icon theme-icon-moon">
<svg class="inline-svg-icon icon-18" fill="currentColor" xmlns="http://www.w3.org/2000/svg"
height="1em" viewBox="0 -960 960 960" width="1em">
Expand Down Expand Up @@ -185,7 +185,7 @@ <h3 class="flex-center-gap-8">
autocomplete="current-password" placeholder=" " data-tour="login-password">
<label for="passwordInput">密碼</label>
<button type="button" id="togglePasswordBtn" class="toggle-password-btn"
aria-label="顯示密碼" title="顯示密碼">
aria-label="顯示密碼" title="顯示密碼" aria-pressed="false">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The aria-pressed attribute should be removed here as well, as the button's label is updated dynamically in the JavaScript logic to reflect the current state.

Suggested change
aria-label="顯示密碼" title="顯示密碼" aria-pressed="false">
aria-label="顯示密碼" title="顯示密碼">

<svg class="inline-svg-icon eye-icon" fill="currentColor"
xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 -960 960 960"
width="1em">
Expand Down Expand Up @@ -534,7 +534,7 @@ <h3 class="section-title">
</footer>
</div>

<script type="module" src="/dist/main-D-w4GNFQ.js"></script>
<script type="module" src="/dist/main-b8YgiUL2.js"></script>
</body>

</html>
2 changes: 1 addition & 1 deletion public/privacy.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<link
href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&family=Noto+Sans+TC:wght@400;500;700&display=swap"
rel="stylesheet">
<link rel="stylesheet" href="/dist/main-bt8E1vXG.css" id="vite-css">
<link rel="stylesheet" href="/dist/main-D2sAJccc.css" id="vite-css">

Copilot AI Apr 9, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public/privacy.html hardcodes a hashed /dist/main-*.css filename. Since the Docker image build only overwrites public/index.html from the frontend build stage (but not public/privacy.html), any change in the Vite output hash (e.g., due to VITE_COMMIT_HASH) can leave privacy.html pointing at a CSS file that doesn’t exist in public/dist, breaking styling for the privacy page. Consider either copying the post-build privacy.html from the frontend build stage into the final image, or commit an un-hashed placeholder href (e.g., /dist/main.css) and rely on scripts/inject-hash.js during build to rewrite it.

Copilot uses AI. Check for mistakes.
<link rel="icon" type="image/x-icon" href="/favicon.ico">
<style>
.privacy-container {
Expand Down
Loading