Skip to content

feature/newspage&optimize - #20

Closed
RhymeJoy wants to merge 15 commits into
mainfrom
feature/newspage&optimize
Closed

RhymeJoy wants to merge 15 commits into
mainfrom
feature/newspage&optimize

Conversation

@RhymeJoy

@RhymeJoy RhymeJoy commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Interactive news page with mail-style posts and wax seal decorations
    • Update log modal accessible via version button
    • Enhanced site navigation with language switcher
    • Social media links (YouTube, Discord, Facebook, X, Instagram) in footer
  • Improvements

    • Refined page layout and container structure for consistency
    • Enhanced responsive design across devices
    • Typography and styling refinements
    • Expanded localization for English and Traditional Chinese

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 15, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

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

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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 /news page 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.

Comment thread i18n/locales/en.json Outdated
Comment thread i18n/locales/en.json Outdated
Comment thread app/pages/news.vue
Comment thread app/pages/news.vue
Comment on lines +260 to +275
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]
}
Comment thread app/components/SiteNav.vue Outdated
Comment thread app/assets/css/global.css Outdated
Comment thread app/pages/news.vue

@bradly0cjw bradly0cjw left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

📋 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 useHead or a reactive class for body scroll locking instead of direct DOM manipulation.

Comment thread i18n/locales/en.json Outdated
"folder": "L-Folder",
"bag": "Tote Bag",
"keychain": "Keychain",
"keychain": "KeychaiJoyn",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[LOW] Typo in 'keychain'.

Suggested change
"keychain": "KeychaiJoyn",
"keychain": "Keychain",

Comment thread app/pages/news.vue Outdated
}
})
.sort((a, b) => {
const dateA = new Date(a.time.replace(/\//g, '-')).getTime()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[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.

Comment thread app/components/SiteNav.vue Outdated
</script>

<template>
<button class="nav-toggle" :class="{ active: isNavOpen }" @click.stop="toggleNav">

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[MEDIUM] Missing accessibility attributes on the navigation toggle button. Consider adding aria-label and aria-expanded.

RhymeJoy and others added 8 commits June 16, 2026 08:45
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 bradly0cjw left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Starting code review...

@bradly0cjw bradly0cjw left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

📋 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 from aria-hidden and the toggles (span) from role="button" and aria-haspopup="true" for better screen reader support.
  • Date Handling: The toTime helper in news.vue is 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.vue is a great move that reduces redundancy across pages.

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1166cef9-5641-4ba1-bb45-b329a050e916

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • ✅ Review completed - (🔄 Check again to review again)
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 14

🧹 Nitpick comments (1)
app/assets/css/global.css (1)

55-65: ⚡ Quick win

Consolidate 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

📥 Commits

Reviewing files that changed from the base of the PR and between d18ec72 and 6d9c776.

⛔ Files ignored due to path filters (10)
  • public/img/123.jpg is excluded by !**/*.jpg
  • public/img/Mascot.png is excluded by !**/*.png
  • public/img/form-b.png is excluded by !**/*.png
  • public/img/form-g.png is excluded by !**/*.png
  • public/img/form-o.png is excluded by !**/*.png
  • public/img/form-p.png is excluded by !**/*.png
  • public/img/leafbtn.png is excluded by !**/*.png
  • public/img/logo1.png is excluded by !**/*.png
  • public/img/paper.jpg is excluded by !**/*.jpg
  • public/img/wood.jpg is excluded by !**/*.jpg
📒 Files selected for processing (19)
  • app/app.vue
  • app/assets/css/font.css
  • app/assets/css/global.css
  • app/assets/css/homepage.css
  • app/assets/css/news.css
  • app/assets/css/ticket.css
  • app/components/SharedBackground.vue
  • app/components/SiteNav.vue
  • app/components/footer.vue
  • app/composables/useLegacyAssets.ts
  • app/pages/index.vue
  • app/pages/news.vue
  • app/pages/ticket.vue
  • i18n/locales/en.json
  • i18n/locales/zh-TW.json
  • public/img/Mascot.avif
  • public/img/Mascot_101.avif
  • public/img/WaxSeal.avif
  • public/img/Why.avif

.container {
position: relative;
width: clamp(50%, 50vw, 1400px);
width: clamp(75%, 50vw, 1400px);

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Suggested change
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.

Comment thread app/assets/css/news.css
Comment on lines +192 to +196
line-clamp: 4;
-webkit-line-clamp: 4;
-webkit-box-orient: vertical;
overflow: hidden;
word-break: break-word;

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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n "word-break:\s*break-word|`@keyframes`\s+whyFloat|animation:\s*whyFloat" app/assets/css/news.css

Repository: 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:


🌐 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:


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

Comment thread app/components/footer.vue
Comment thread app/components/footer.vue
Comment on lines +340 to +355
<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"
>

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Suggested change
<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.

Comment on lines +39 to +71
<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>

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Comment thread app/pages/news.vue
Comment on lines +402 to +408
document.body.style.overflow = 'hidden'
}

function closeMail() {
selectedPost.value = null
document.body.style.overflow = ''
}

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Comment thread app/pages/news.vue
Comment on lines +473 to +482
<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)"
>

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Comment thread i18n/locales/en.json
Comment on lines +136 to +138
"copyLink": "Copy Post Link",
"openPost": "Open Original Post"
},

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Comment thread i18n/locales/en.json
Comment on lines +148 to +149
"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",

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

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.

Comment thread i18n/locales/zh-TW.json
"posts": [
{
"id": "post_001",
"title": "TWBC台灣馬聚2026計畫開起 !",

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

修正標題錯字(開起 → 開啟)。

這是使用者可見文案,建議在合併前修正。

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.

Suggested change
"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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants