Conversation
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
twbc-site | 6d9c776 | Commit Preview URL Branch Preview URL |
Jun 17 2026, 06:29 AM |
There was a problem hiding this comment.
Pull request overview
This PR introduces a new “News” page and refactors shared layout elements (background, navigation, footer) so they’re rendered consistently across pages, alongside substantial i18n content additions to support the new page and updated navigation labels.
Changes:
- Add
/newspage with interactive “mail” UI and localized post content sourced from i18n messages. - Centralize global layout elements in
app/app.vue(SharedBackground + SiteNav + Footer), removing per-page duplicates. - Update/expand i18n keys (menu/nav/news/about/schedule) and adjust related styling across pages.
Reviewed changes
Copilot reviewed 15 out of 29 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| i18n/locales/zh-TW.json | Adds menu/news/nav/about/schedule strings and localized news post data. |
| i18n/locales/en.json | Adds menu/news/nav/about/schedule strings and English news post data; updates SEO/site naming and ticket wording/prices. |
| app/pages/ticket.vue | Removes per-page background/footer and tweaks feature list separator by locale. |
| app/pages/news.vue | New News page implementation (modal, mail list, seal animation, opened-state storage). |
| app/pages/index.vue | Removes inline nav/background/footer logic now handled globally. |
| app/composables/useLegacyAssets.ts | Adds helper to load legacy styles/scripts for integrated legacy pages. |
| app/components/SiteNav.vue | New shared navigation component with locale switching and apply submenu. |
| app/components/SharedBackground.vue | Adjusts mobile background positioning. |
| app/components/footer.vue | Enhances footer (sanitizes external links, ClientOnly quotes, adds update-log modal + version trigger). |
| app/assets/css/ticket.css | Adjusts ticket page layout/styling and adds container offset for global nav. |
| app/assets/css/news.css | New styling for the News page (mail list + modal + floating mascot). |
| app/assets/css/homepage.css | Removes nav/lang-switcher CSS now handled by SiteNav; adjusts container sizing/margins. |
| app/assets/css/global.css | Adds page transitions/layout helpers and legacy-page overrides; adjusts overflow handling. |
| app/assets/css/font.css | Adds font utility classes (and commented-out font-face). |
| app/app.vue | Renders SharedBackground, SiteNav, and Footer globally around NuxtPage. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| return Math.round(randomBetween(-50, 50)) | ||
| } | ||
|
|
||
| function getSealData(postId: string) { | ||
| if (!sealMap.value[postId]) { | ||
| const crack = createRandomCrack() | ||
|
|
||
| sealMap.value[postId] = { | ||
| angle: createSealAngle(postId), | ||
| topClip: crack.topClip, | ||
| bottomClip: crack.bottomClip | ||
| } | ||
| } | ||
|
|
||
| return sealMap.value[postId] | ||
| } |
bradly0cjw
left a comment
There was a problem hiding this comment.
📋 Review Summary
This PR introduces a new News page and centralizes global layout elements. The implementation is solid, but there are a few minor issues regarding typo, date parsing, and accessibility.
🔍 General Feedback
- Good job on centralizing the layout in
app.vue. - The News page UI looks very polished.
- Consider using
useHeador a reactive class for body scroll locking instead of direct DOM manipulation.
| "folder": "L-Folder", | ||
| "bag": "Tote Bag", | ||
| "keychain": "Keychain", | ||
| "keychain": "KeychaiJoyn", |
There was a problem hiding this comment.
[LOW] Typo in 'keychain'.
| "keychain": "KeychaiJoyn", | |
| "keychain": "Keychain", |
| } | ||
| }) | ||
| .sort((a, b) => { | ||
| const dateA = new Date(a.time.replace(/\//g, '-')).getTime() |
There was a problem hiding this comment.
[MEDIUM] Using new Date() with replace(/\//g, '-') can be inconsistent across browsers for 'MM-DD-YYYY' formats. Standardizing to ISO (YYYY-MM-DD) is recommended.
| </script> | ||
|
|
||
| <template> | ||
| <button class="nav-toggle" :class="{ active: isNavOpen }" @click.stop="toggleNav"> |
There was a problem hiding this comment.
[MEDIUM] Missing accessibility attributes on the navigation toggle button. Consider adding aria-label and aria-expanded.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
bradly0cjw
left a comment
There was a problem hiding this comment.
📋 Review Summary
The PR successfully implements the new News page with a unique interactive mailbox UI and centralizes the site layout for better maintainability. The refactoring of app.vue and the addition of the SiteNav component are significant improvements to the codebase structure.
🔍 General Feedback
- The interactive "wax seal" and "mailbox" UI on the News page is creative and well-implemented.
- Accessibility: While the navigation toggle now has ARIA attributes, the submenus (
ul.submenu) could benefit fromaria-hiddenand the toggles (span) fromrole="button"andaria-haspopup="true"for better screen reader support. - Date Handling: The
toTimehelper innews.vueis a good improvement for handling mixed date formats, but standardizing on ISO 8601 in the JSON files would be a more robust long-term solution. - Refactoring: Centralizing the layout in
app.vueis a great move that reduces redundancy across pages.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 14
🧹 Nitpick comments (1)
app/assets/css/global.css (1)
55-65: ⚡ Quick winConsolidate duplicated
.page-*transition rules.Lines 55-65 and Lines 92-105 define overlapping selectors with different transforms; the later block overrides parts of the earlier one. Keeping one canonical block will avoid accidental animation drift.
Suggested fix
-.page-enter-from, -.page-leave-to { - opacity: 0; - transform: translateY(18px); -} - -.page-enter-to, -.page-leave-from { - opacity: 1; - transform: translateY(0); -} +/* Keep a single transition definition set below to avoid overrides */Also applies to: 92-105
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/assets/css/global.css` around lines 55 - 65, Identify the two blocks of page transition rules in the CSS file: the first block containing .page-enter-from, .page-leave-to with opacity and transform translateY(18px), and the second block at lines 92-105 containing .page-enter-to, .page-leave-from with opacity and transform translateY(0). Review both blocks to understand which properties are duplicated and which are overridden, then consolidate them into a single canonical block that defines all the selectors (.page-enter-from, .page-leave-to, .page-enter-to, .page-leave-from) with their correct final values, and remove the duplicate block to prevent animation drift.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/assets/css/homepage.css`:
- Line 18: The clamp() function in the width property of the homepage.css file
uses incompatible bounds where the minimum value (75%) can exceed the maximum
value (1400px) on ultra-wide screens, defeating the purpose of the cap.
Restructure the clamp() function to ensure the minimum bound is always less than
or equal to the maximum bound (1400px), either by reducing the minimum
percentage value, constraining the minimum with a max() function, or adjusting
the bounds to properly scale the container while maintaining the 1400px ceiling
across all screen sizes.
In `@app/assets/css/news.css`:
- Around line 192-196: Replace the deprecated `word-break: break-word` property
in the line-clamp section with `overflow-wrap: anywhere`. Additionally, locate
the keyframes definition named `whyFloat` (around lines 464-471) and rename it
to kebab-case `why-float`. Update any CSS rules that reference this animation to
use the new kebab-case name `why-float` instead of `whyFloat` to comply with CSS
naming conventions.
In `@app/components/footer.vue`:
- Around line 78-165: The updateLogs array contains hardcoded Chinese strings
that are not being translated when the locale changes, breaking bilingual
functionality. Move all Chinese text from the updateLogs array (version, date,
and changes fields) into i18n resource files, then update the updateLogs array
to use i18n keys to reference these translations dynamically based on the
current locale. Apply the same fix to the other hardcoded Chinese content
mentioned at lines 348-377.
- Around line 340-355: The modal container div with class update-log-modal is
missing proper dialog semantics and keyboard accessibility. Add the
role="dialog" attribute and aria-modal="true" to the outer modal container div
to establish proper dialog semantics. Additionally, add a `@keydown.esc` event
listener to the same modal container that calls the closeUpdateLog method to
allow users to dismiss the modal using the Escape key.
In `@app/components/SiteNav.vue`:
- Around line 52-53: The span element at line 52 that triggers
toggleSubmenu('apply') via `@click` is not keyboard-accessible because span
elements are not focusable by default. Convert this span element to a button
element, or if changing the element type is not feasible, add role="button",
tabindex="0", and `@keydown.enter` and `@keydown.space` handlers to make it
keyboard-navigable. Apply this same fix to the other occurrences mentioned at
lines 184-186 to ensure all submenu triggers are accessible to keyboard users.
- Around line 54-56: The three external links in SiteNav.vue (for
'apply.volunteer', 'apply.vendor', and 'apply.event') have target="_blank"
attributes but are missing the rel="noopener noreferrer" attribute, which is a
security vulnerability when opening links in new tabs. Add rel="noopener
noreferrer" to each of the three anchor tags that open Google Forms in a new tab
to prevent the opened page from accessing the window object and protect against
security risks.
- Around line 39-71: The mobile navigation drawer remains open after a user
clicks a link because the close handler only responds to outside clicks. Add a
click handler to close the navigation when links are selected. Specifically, add
`@click`="isNavOpen = false" (or similar handler) to each NuxtLink component in
the navigation (the home, news, and ticket links around lines 40-42, and the
language switcher NuxtLink elements around lines 55-60) so that when users click
these links, isNavOpen is set to false and the drawer closes after route
navigation.
In `@app/composables/useLegacyAssets.ts`:
- Around line 9-17: The loadScript function creates a Promise that can block
indefinitely if a script request hangs and never fires the onload or onerror
event. Add a timeout mechanism using setTimeout inside the Promise constructor
that rejects with an error message after a reasonable duration (e.g., 30
seconds). Ensure proper cleanup by clearing the timeout in both the
script.onload and script.onerror handlers, and also clear the timeout if the
Promise is rejected due to timeout expiration. This prevents subsequent scripts
from being blocked and allows the application to continue functioning even if a
legacy script fails to respond.
- Around line 40-43: The onBeforeUnmount hook in useLegacyAssets removes all
legacy scripts globally without instance scoping, which causes interference when
multiple instances of the composable run concurrently. Generate a unique
instance identifier at the beginning of the composable, add this identifier as
an instance-specific data attribute (like data-legacy-owner="<uniqueId>") when
scripts are created, and then update the querySelectorAll in the onBeforeUnmount
hook to only remove scripts matching both the legacy page script marker and the
instance-specific owner attribute.
In `@app/pages/news.vue`:
- Around line 402-408: In both the modal open section (around line 402) and the
closeMail function (around line 407), you need to lock scroll on both the html
and body elements. When setting document.body.style.overflow to 'hidden' to open
the modal, also set document.documentElement.style.overflow to 'hidden' to lock
the html element's scroll. When resetting document.body.style.overflow to empty
string in the closeMail function, also reset
document.documentElement.style.overflow to empty string. Apply the same changes
to lines 444-445 where similar scroll locking occurs.
- Around line 473-482: The article element in the mail card component only
responds to mouse events (`@mouseenter` and `@click`) which excludes keyboard users.
Make the mail card keyboard operable by adding a tabindex attribute to make the
article focusable, then add keyboard event handlers (such as `@keydown`) to detect
Enter or Space key presses and trigger the same openMail(post) function that the
`@click` handler uses. Additionally, consider adding `@focus` and `@blur` event
handlers to replicate the mouse enter/leave behavior (moveWhyToMail and
startWhyIdleTimer) for keyboard navigation.
In `@i18n/locales/en.json`:
- Around line 136-138: The translation key "news.actions.openPost" is properly
defined, but all posts in the data still use placeholder URLs set to "#". Locate
all post objects in the file that contain a "url" property and replace the "#"
placeholder values with actual, valid post URLs. This issue appears across
multiple post definitions (as indicated by the line range 143-200), so ensure
all posts have real URLs instead of the placeholder value to make the
open-original-post action functional.
- Around line 148-149: The EN news feed in the i18n/locales/en.json file
contains inconsistent date formats, with some entries using MM/DD/YYYY format
(like the "01/19/2026" shown) and others using YYYY/MM/DD format (such as
post_007). To fix this, standardize all "time" field values across all news
entries to use a single consistent date format, preferably YYYY/MM/DD to align
with existing entries like post_007. Apply this normalization to all affected
entries including those at lines 148-149, 156-157, 164-165, 172-173, 180-181,
188-189, and 196-197.
In `@i18n/locales/zh-TW.json`:
- Line 152: The title field in the zh-TW locale file contains a typo where "開起"
should be corrected to "開啟". Locate the title string with the value containing
"台灣馬聚2026計畫" and replace the incorrect character "開起" with the correct
traditional Chinese character "開啟" to fix the user-facing text.
---
Nitpick comments:
In `@app/assets/css/global.css`:
- Around line 55-65: Identify the two blocks of page transition rules in the CSS
file: the first block containing .page-enter-from, .page-leave-to with opacity
and transform translateY(18px), and the second block at lines 92-105 containing
.page-enter-to, .page-leave-from with opacity and transform translateY(0).
Review both blocks to understand which properties are duplicated and which are
overridden, then consolidate them into a single canonical block that defines all
the selectors (.page-enter-from, .page-leave-to, .page-enter-to,
.page-leave-from) with their correct final values, and remove the duplicate
block to prevent animation drift.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 159aabc2-fb02-46b1-8b90-607610f19466
⛔ Files ignored due to path filters (10)
public/img/123.jpgis excluded by!**/*.jpgpublic/img/Mascot.pngis excluded by!**/*.pngpublic/img/form-b.pngis excluded by!**/*.pngpublic/img/form-g.pngis excluded by!**/*.pngpublic/img/form-o.pngis excluded by!**/*.pngpublic/img/form-p.pngis excluded by!**/*.pngpublic/img/leafbtn.pngis excluded by!**/*.pngpublic/img/logo1.pngis excluded by!**/*.pngpublic/img/paper.jpgis excluded by!**/*.jpgpublic/img/wood.jpgis excluded by!**/*.jpg
📒 Files selected for processing (19)
app/app.vueapp/assets/css/font.cssapp/assets/css/global.cssapp/assets/css/homepage.cssapp/assets/css/news.cssapp/assets/css/ticket.cssapp/components/SharedBackground.vueapp/components/SiteNav.vueapp/components/footer.vueapp/composables/useLegacyAssets.tsapp/pages/index.vueapp/pages/news.vueapp/pages/ticket.vuei18n/locales/en.jsoni18n/locales/zh-TW.jsonpublic/img/Mascot.avifpublic/img/Mascot_101.avifpublic/img/WaxSeal.avifpublic/img/Why.avif
| .container { | ||
| position: relative; | ||
| width: clamp(50%, 50vw, 1400px); | ||
| width: clamp(75%, 50vw, 1400px); |
There was a problem hiding this comment.
Fix clamp() bounds to keep the desktop container truly capped.
width: clamp(75%, 50vw, 1400px) can exceed 1400px on ultra-wide screens because the minimum bound grows with viewport width and eventually becomes larger than the max bound.
Suggested fix
- width: clamp(75%, 50vw, 1400px);
+ width: min(1400px, max(75%, 50vw));📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| width: clamp(75%, 50vw, 1400px); | |
| width: min(1400px, max(75%, 50vw)); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/assets/css/homepage.css` at line 18, The clamp() function in the width
property of the homepage.css file uses incompatible bounds where the minimum
value (75%) can exceed the maximum value (1400px) on ultra-wide screens,
defeating the purpose of the cap. Restructure the clamp() function to ensure the
minimum bound is always less than or equal to the maximum bound (1400px), either
by reducing the minimum percentage value, constraining the minimum with a max()
function, or adjusting the bounds to properly scale the container while
maintaining the 1400px ceiling across all screen sizes.
| line-clamp: 4; | ||
| -webkit-line-clamp: 4; | ||
| -webkit-box-orient: vertical; | ||
| overflow: hidden; | ||
| word-break: break-word; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
rg -n "word-break:\s*break-word|`@keyframes`\s+whyFloat|animation:\s*whyFloat" app/assets/css/news.cssRepository: TW-BronyCon/twbc-site
Length of output: 176
🌐 Web query:
CSS word-break break-word deprecated overflow-wrap
💡 Result:
The CSS word-break: break-word value is deprecated [1][2][3]. It has been maintained primarily for legacy compatibility and is superseded by the overflow-wrap property [4][5]. Key points regarding its deprecation and usage: 1. Deprecation Status: The word-break: break-word value is considered discouraged and deprecated by the CSS Working Group [1][4][6]. It is not appropriate for new stylesheets [6]. 2. Preferred Alternative: You should use the overflow-wrap property instead [4][5]. Specifically, overflow-wrap: anywhere is the modern, standard-compliant way to achieve the behavior previously associated with word-break: break-word [1][7][8]. 3. Behavioral Differences: When word-break: break-word is used, browsers treat it as equivalent to word-break: normal combined with overflow-wrap: anywhere [1][2][8]. This effectively forces an overflow-wrap behavior regardless of the actual overflow-wrap property value set on the element [2][9]. Importantly, overflow-wrap: anywhere differs from overflow-wrap: break-word in how it affects min-content intrinsic size calculations: anywhere considers the potential soft wrap opportunities, whereas break-word does not [1][8][10]. In summary, for controlling how text breaks to prevent overflow, you should prioritize overflow-wrap (e.g., overflow-wrap: anywhere or overflow-wrap: break-word) over the deprecated word-break: break-word [4][10][11].
Citations:
- 1: https://www.w3.org/TR/css-text-3/
- 2: https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/word-break
- 3: https://www.w3schools.com/cssref/css3_pr_word-break.php
- 4: https://web-platform-dx.github.io/web-features-explorer/features/word-break-break-word/
- 5: https://css-tricks.com/almanac/properties/o/overflow-wrap/
- 6: [css-text-3] Normatively disallow break-word w3c/csswg-drafts#4087
- 7: https://stackoverflow.com/questions/17259916/difference-between-overflow-wrap-and-word-break
- 8: https://stackoverflow.com/questions/56618668/do-overflow-wrap-break-word-and-word-break-break-word-ever-behave-differen
- 9: https://github.com/mdn/content/blob/main/files/en-us/web/css/reference/properties/word-break/index.md
- 10: https://github.com/mdn/content/blob/main/files/en-us/web/css/reference/properties/overflow-wrap/index.md
- 11: https://github.com/mdn/content/blob/main/files/en-us/web/css/guides/text/wrapping_breaking_text/index.md
🌐 Web query:
CSS keyframes naming convention kebab-case camelCase stylelint
💡 Result:
There is no single web standard or CSS specification that mandates either kebab-case or camelCase for @keyframes names [1][2]. Both are technically valid identifiers in CSS [1]. However, kebab-case is widely considered the community standard for CSS naming conventions, aligning with the casing style of built-in CSS properties and values [1]. Stylelint provides a specific rule, keyframes-name-pattern, to enforce whichever naming convention your team chooses [3][4]. Key Points: 1. Technical Validity: CSS allows both kebab-case (e.g., slide-in) and camelCase (e.g., slideIn) as valid custom identifiers for keyframe names [1]. 2. Community Convention: Kebab-case is strongly preferred in the broader web development community [5][6]. Using kebab-case maintains consistency with CSS’s native syntax and helps avoid potential confusion when interacting with tools or frameworks that may default to or expect kebab-cased names [6]. 3. Enforcing Conventions with Stylelint: You can use the keyframes-name-pattern rule in your.stylelintrc file to enforce your preferred convention [3]. Example configuration: To enforce kebab-case: { "rules": { "keyframes-name-pattern": "^[a-z0-9-]+$" } } To enforce camelCase: { "rules": { "keyframes-name-pattern": "^[a-z][a-zA-Z0-9]+$" } } The keyframes-name-pattern rule accepts a regular expression string [3][7]. If you are working in environments like Tailwind CSS, be aware that some framework versions may have specific requirements or historical regressions that favor kebab-case, making it the safer choice for compatibility [5][6].
Citations:
- 1: https://developer.chrome.com/docs/css-ui/css-names.md.txt
- 2: SebastianZ/csswg-drafts@0ac66d4
- 3: https://stylelint.io/user-guide/rules/keyframes-name-pattern/
- 4: https://stylelint.io/user-guide/rules
- 5: cannot use camelCase for animation or keyframe name tailwindlabs/tailwindcss#10719
- 6: v4 Regression: Keyframe animations defined in JS must be referred to using kebab case, even if defined in camelCase tailwindlabs/tailwindcss#16092
- 7: https://github.com/stylelint/stylelint/blob/main/lib/rules/keyframes-name-pattern/README.md
Replace deprecated word-break: break-word with overflow-wrap: anywhere and rename keyframes to kebab-case.
Line 196 uses word-break: break-word, which is deprecated by the CSS Working Group and should be replaced with overflow-wrap: anywhere. Line 464-471 use camelCase for the keyframe name (whyFloat), which violates the community standard CSS naming convention (kebab-case) that is enforced by stylelint.
Suggested fix
.preview {
...
- word-break: break-word;
+ overflow-wrap: anywhere;
}
...
-.why {
+.why {
...
- animation: whyFloat 5s ease-in-out infinite;
+ animation: why-float 5s ease-in-out infinite;
}
...
-@keyframes whyFloat {
+@keyframes why-float {🧰 Tools
🪛 Stylelint (17.13.0)
[error] 196-196: Deprecated keyword "break-word" for property "word-break" (declaration-property-value-keyword-no-deprecated)
(declaration-property-value-keyword-no-deprecated)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/assets/css/news.css` around lines 192 - 196, Replace the deprecated
`word-break: break-word` property in the line-clamp section with `overflow-wrap:
anywhere`. Additionally, locate the keyframes definition named `whyFloat`
(around lines 464-471) and rename it to kebab-case `why-float`. Update any CSS
rules that reference this animation to use the new kebab-case name `why-float`
instead of `whyFloat` to comply with CSS naming conventions.
Source: Linters/SAST tools
| <div | ||
| v-if="showUpdateLog" | ||
| class="update-log-modal" | ||
| @click.self="closeUpdateLog" | ||
| > | ||
| <div class="update-log-box"> | ||
|
|
||
| <div class="update-log-header"> | ||
| <h2>✦ 更新日誌 ✦</h2> | ||
|
|
||
| <button | ||
| class="update-log-close" | ||
| type="button" | ||
| aria-label="Close update log" | ||
| @click="closeUpdateLog" | ||
| > |
There was a problem hiding this comment.
Add dialog semantics and keyboard-close support to the modal.
Lines 340-355 render a modal overlay but it lacks explicit dialog semantics. Add role="dialog"/aria-modal and ensure Escape closes the dialog in a keyboard-only flow.
Suggested fix
<div
v-if="showUpdateLog"
class="update-log-modal"
+ role="dialog"
+ aria-modal="true"
+ aria-labelledby="update-log-title"
+ tabindex="-1"
+ `@keydown.esc`="closeUpdateLog"
`@click.self`="closeUpdateLog"
>
...
- <h2>✦ 更新日誌 ✦</h2>
+ <h2 id="update-log-title">✦ 更新日誌 ✦</h2>
...
<button
class="update-log-close"
type="button"
aria-label="Close update log"
+ autofocus
`@click`="closeUpdateLog"
>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <div | |
| v-if="showUpdateLog" | |
| class="update-log-modal" | |
| @click.self="closeUpdateLog" | |
| > | |
| <div class="update-log-box"> | |
| <div class="update-log-header"> | |
| <h2>✦ 更新日誌 ✦</h2> | |
| <button | |
| class="update-log-close" | |
| type="button" | |
| aria-label="Close update log" | |
| @click="closeUpdateLog" | |
| > | |
| <div | |
| v-if="showUpdateLog" | |
| class="update-log-modal" | |
| role="dialog" | |
| aria-modal="true" | |
| aria-labelledby="update-log-title" | |
| tabindex="-1" | |
| `@keydown.esc`="closeUpdateLog" | |
| `@click.self`="closeUpdateLog" | |
| > | |
| <div class="update-log-box"> | |
| <div class="update-log-header"> | |
| <h2 id="update-log-title">✦ 更新日誌 ✦</h2> | |
| <button | |
| class="update-log-close" | |
| type="button" | |
| aria-label="Close update log" | |
| autofocus | |
| `@click`="closeUpdateLog" | |
| > |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/components/footer.vue` around lines 340 - 355, The modal container div
with class update-log-modal is missing proper dialog semantics and keyboard
accessibility. Add the role="dialog" attribute and aria-modal="true" to the
outer modal container div to establish proper dialog semantics. Additionally,
add a `@keydown.esc` event listener to the same modal container that calls the
closeUpdateLog method to allow users to dismiss the modal using the Escape key.
| <nav class="top-nav" :class="{ open: isNavOpen }"> | ||
| <NuxtLink :to="localePath('/')">{{ $t('menu.home') }}</NuxtLink> | ||
| <NuxtLink :to="localePath('/news')">{{ $t('menu.news') }}</NuxtLink> | ||
| <NuxtLink :to="localePath('/ticket')">{{ $t('menu.ticket') }}</NuxtLink> | ||
| <NuxtLink v-if="false" :to="localePath('/about')">{{ $t('menu.about') }}</NuxtLink> | ||
| <span v-else class="nav-coming-soon">{{ $t('tba.about') }}</span> | ||
| <NuxtLink v-if="false" :to="localePath('/schedule')">{{ $t('menu.schedule') }}</NuxtLink> | ||
| <span v-else class="nav-coming-soon">{{ $t('tba.schedule') }}</span> | ||
| <NuxtLink v-if="false" :to="localePath('/venue')">{{ $t('menu.venue') }}</NuxtLink> | ||
| <span v-else class="nav-coming-soon">{{ $t('tba.venue') }}</span> | ||
|
|
||
| <ul class="menu" style="transform: translateY(-1px);"> | ||
| <li class="menu-group" :class="{ open: openSubmenu === 'apply' }"> | ||
| <span @click="toggleSubmenu('apply')">{{ $t('menu.apply') }}</span> | ||
| <ul class="submenu"> | ||
| <li><a href="https://docs.google.com/forms/d/e/1FAIpQLSc-oA7Cbqis71Azd_7ZF2i6e7OqMr9h5pXCwP8D7IZFfIiCFg/viewform" target="_blank">{{ $t('apply.volunteer') }}</a></li> | ||
| <li><a href="https://docs.google.com/forms/d/e/1FAIpQLSe_AH-DG67VmJxCAm8Dt07m8IosoE765XZKukSn5AIWN-HIvQ/viewform" target="_blank">{{ $t('apply.vendor') }}</a></li> | ||
| <li><a href="https://docs.google.com/forms/d/e/1FAIpQLSf1h5OaWHYf2cdqkEwWk_SWcNioLj89ilOAtr1g1sbupTPk1w/viewform" target="_blank">{{ $t('apply.event') }}</a></li> | ||
| </ul> | ||
| </li> | ||
| </ul> | ||
|
|
||
| <div class="lang-switcher"> | ||
| <NuxtLink | ||
| v-for="item in (locales as any)" | ||
| :key="item.code" | ||
| :to="switchLocalePath(item.code)" | ||
| :class="{ active: locale === item.code }" | ||
| > | ||
| {{ item.name }} | ||
| </NuxtLink> | ||
| </div> | ||
| </nav> |
There was a problem hiding this comment.
Close the mobile nav after a link is selected.
Because the close handler only reacts to outside clicks, selecting links inside .top-nav (Lines 40-69) can leave the drawer open after route change in the persistent app shell.
Suggested fix
+const closeAllMenus = () => {
+ isNavOpen.value = false
+ openSubmenu.value = null
+}- <NuxtLink :to="localePath('/')">{{ $t('menu.home') }}</NuxtLink>
+ <NuxtLink :to="localePath('/')" `@click`="closeAllMenus">{{ $t('menu.home') }}</NuxtLink>
...
- <NuxtLink :to="localePath('/news')">{{ $t('menu.news') }}</NuxtLink>
+ <NuxtLink :to="localePath('/news')" `@click`="closeAllMenus">{{ $t('menu.news') }}</NuxtLink>
...
- <NuxtLink :to="localePath('/ticket')">{{ $t('menu.ticket') }}</NuxtLink>
+ <NuxtLink :to="localePath('/ticket')" `@click`="closeAllMenus">{{ $t('menu.ticket') }}</NuxtLink>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/components/SiteNav.vue` around lines 39 - 71, The mobile navigation
drawer remains open after a user clicks a link because the close handler only
responds to outside clicks. Add a click handler to close the navigation when
links are selected. Specifically, add `@click`="isNavOpen = false" (or similar
handler) to each NuxtLink component in the navigation (the home, news, and
ticket links around lines 40-42, and the language switcher NuxtLink elements
around lines 55-60) so that when users click these links, isNavOpen is set to
false and the drawer closes after route navigation.
| document.body.style.overflow = 'hidden' | ||
| } | ||
|
|
||
| function closeMail() { | ||
| selectedPost.value = null | ||
| document.body.style.overflow = '' | ||
| } |
There was a problem hiding this comment.
Lock both html and body scroll when the modal is open.
Line 402 and Line 407 only mutate document.body.style.overflow. With html overflow set to visible globally, background scrolling can remain possible while the modal is open.
Suggested fix
+function setPageScrollLock(locked: boolean) {
+ const value = locked ? 'hidden' : ''
+ document.documentElement.style.overflow = value
+ document.body.style.overflow = value
+}
+
function openMail(post: NewsPost) {
openedMap.value[post.id] = true
saveOpenedMap()
selectedPost.value = post
- document.body.style.overflow = 'hidden'
+ setPageScrollLock(true)
}
function closeMail() {
selectedPost.value = null
- document.body.style.overflow = ''
+ setPageScrollLock(false)
}
...
onBeforeUnmount(() => {
...
- document.body.style.overflow = ''
+ setPageScrollLock(false)Also applies to: 444-445
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/pages/news.vue` around lines 402 - 408, In both the modal open section
(around line 402) and the closeMail function (around line 407), you need to lock
scroll on both the html and body elements. When setting
document.body.style.overflow to 'hidden' to open the modal, also set
document.documentElement.style.overflow to 'hidden' to lock the html element's
scroll. When resetting document.body.style.overflow to empty string in the
closeMail function, also reset document.documentElement.style.overflow to empty
string. Apply the same changes to lines 444-445 where similar scroll locking
occurs.
| <article | ||
| v-for="post in posts" | ||
| :key="post.id" | ||
| :ref="el => setMailRef(post.id, el)" | ||
| class="mail" | ||
| :class="{ opened: openedMap[post.id] }" | ||
| @mouseenter="moveWhyToMail(post.id)" | ||
| @mouseleave="startWhyIdleTimer" | ||
| @click="openMail(post)" | ||
| > |
There was a problem hiding this comment.
Make mail cards operable via keyboard.
Lines 473-482 rely on mouse enter/click only. Keyboard users currently cannot open a post from the list.
Suggested fix
<article
v-for="post in posts"
:key="post.id"
:ref="el => setMailRef(post.id, el)"
class="mail"
:class="{ opened: openedMap[post.id] }"
+ role="button"
+ tabindex="0"
+ :aria-label="post.title"
`@mouseenter`="moveWhyToMail(post.id)"
`@mouseleave`="startWhyIdleTimer"
`@click`="openMail(post)"
+ `@keydown.enter.prevent`="openMail(post)"
+ `@keydown.space.prevent`="openMail(post)"
>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/pages/news.vue` around lines 473 - 482, The article element in the mail
card component only responds to mouse events (`@mouseenter` and `@click`) which
excludes keyboard users. Make the mail card keyboard operable by adding a
tabindex attribute to make the article focusable, then add keyboard event
handlers (such as `@keydown`) to detect Enter or Space key presses and trigger the
same openMail(post) function that the `@click` handler uses. Additionally,
consider adding `@focus` and `@blur` event handlers to replicate the mouse
enter/leave behavior (moveWhyToMail and startWhyIdleTimer) for keyboard
navigation.
| "copyLink": "Copy Post Link", | ||
| "openPost": "Open Original Post" | ||
| }, |
There was a problem hiding this comment.
news.actions.openPost is defined, but every post URL is still a placeholder.
All posts currently use "url": "#", so the open-original-post action has no real destination.
Also applies to: 143-200
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@i18n/locales/en.json` around lines 136 - 138, The translation key
"news.actions.openPost" is properly defined, but all posts in the data still use
placeholder URLs set to "#". Locate all post objects in the file that contain a
"url" property and replace the "#" placeholder values with actual, valid post
URLs. This issue appears across multiple post definitions (as indicated by the
line range 143-200), so ensure all posts have real URLs instead of the
placeholder value to make the open-original-post action functional.
| "time": "01/19/2026", | ||
| "content": "Hello everypony!\nWe are planning to hold the first-ever TWBC Taiwan BronyCon in Taipei this summer.\nOur first community survey is now open, and we warmly welcome everypony to participate! (*´▽`*)\n\nSurvey:\nhttps://docs.google.com/forms/d/e/1FAIpQLSex0IRX2WI93OMVNNuxCp0YLPKz9dX38Uz_aiVeY5Ku5-rpSg/alreadyresponded", |
There was a problem hiding this comment.
Normalize EN news date format to one pattern.
The EN feed mixes MM/DD/YYYY and YYYY/MM/DD (e.g., post_007), which makes downstream date handling brittle.
Suggested fix
- "time": "2026/05/17",
+ "time": "05/17/2026",Also applies to: 156-157, 164-165, 172-173, 180-181, 188-189, 196-197
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@i18n/locales/en.json` around lines 148 - 149, The EN news feed in the
i18n/locales/en.json file contains inconsistent date formats, with some entries
using MM/DD/YYYY format (like the "01/19/2026" shown) and others using
YYYY/MM/DD format (such as post_007). To fix this, standardize all "time" field
values across all news entries to use a single consistent date format,
preferably YYYY/MM/DD to align with existing entries like post_007. Apply this
normalization to all affected entries including those at lines 148-149, 156-157,
164-165, 172-173, 180-181, 188-189, and 196-197.
| "posts": [ | ||
| { | ||
| "id": "post_001", | ||
| "title": "TWBC台灣馬聚2026計畫開起 !", |
There was a problem hiding this comment.
修正標題錯字(開起 → 開啟)。
這是使用者可見文案,建議在合併前修正。
Suggested fix
- "title": "TWBC台灣馬聚2026計畫開起 !",
+ "title": "TWBC台灣馬聚2026計畫開啟 !",📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "title": "TWBC台灣馬聚2026計畫開起 !", | |
| "title": "TWBC台灣馬聚2026計畫開啟 !", |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@i18n/locales/zh-TW.json` at line 152, The title field in the zh-TW locale
file contains a typo where "開起" should be corrected to "開啟". Locate the title
string with the value containing "台灣馬聚2026計畫" and replace the incorrect
character "開起" with the correct traditional Chinese character "開啟" to fix the
user-facing text.
Summary by CodeRabbit
New Features
Improvements