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
3 changes: 3 additions & 0 deletions .jules/palette.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,6 @@
## $(date +%Y-%m-%d) - Prevent accidental data loss in inline editors
**Learning:** Forms that take a long time to fill out (like a WBS editor) are prone to accidental closure by users pressing `Escape` or clicking cancel. This causes immediate data loss without any warning, resulting in frustration.
**Action:** When working on editors that can be dismissed, track whether the user has modified any fields compared to their initial state. If there are changes, intercept the close action and present a confirmation dialog (`window.confirm`) to ensure they really want to discard their edits. Bypass this for intentional saves or explicit data overrides.
## 2026-08-05 - λΉ„λŒ€ν™”ν˜• μš”μ†Œμ˜ 툴팁 μ ‘κ·Όμ„±
**Learning:** λΉ„λŒ€ν™”ν˜• μš”μ†Œ(`div`, `span` λ“±)에 `title`μ΄λ‚˜ `aria-label`둜 νˆ΄νŒμ„ μ œκ³΅ν•  λ•Œ, ν‚€λ³΄λ“œ μ‚¬μš©μžλŠ” νƒ­μœΌλ‘œ 포컀슀λ₯Ό 이동할 수 μ—†μ–΄ ν•΄λ‹Ή 정보λ₯Ό 인지할 수 μ—†λŠ” λ¬Έμ œκ°€ λ°œμƒν•¨.
**Action:** λΉ„λŒ€ν™”ν˜• μš”μ†ŒλΌλ„ 툴팁 λ“± λΆ€κ°€ 정보가 ν¬ν•¨λ˜μ–΄ μžˆλ‹€λ©΄ `tabindex="0"`을 μΆ”κ°€ν•˜μ—¬ 포컀슀λ₯Ό 받을 수 μžˆλ„λ‘ ν•˜κ³ , `:focus-visible`을 톡해 λͺ…ν™•ν•œ μ‹œκ°μ  ν”Όλ“œλ°±μ„ μ œκ³΅ν•΄μ•Ό 함.
1 change: 1 addition & 0 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -999,6 +999,7 @@ function createStatusCellContent(progressState) {
if (progressState.description) {
badge.title = progressState.description;
badge.setAttribute('aria-label', `${progressState.label} - ${progressState.description}`);
badge.tabIndex = 0;
}
return badge;
}
Expand Down
6 changes: 3 additions & 3 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,18 @@ <h1>ScopeWeave Planner</h1>
<span>기쀀일</span>
<input data-testid="base-date-input" id="base-date" type="date" />
</label>
<div class="meta-value-card" title="ν”„λ‘œμ νŠΈμ˜ μž‘μ—… κΈ°κ°„(일수) ν•©κ³„μž…λ‹ˆλ‹€.">
<div class="meta-value-card" title="ν”„λ‘œμ νŠΈμ˜ μž‘μ—… κΈ°κ°„(일수) ν•©κ³„μž…λ‹ˆλ‹€." tabindex="0">
<span class="meta-label">μ „μ²΄μΌμˆ˜</span>
<strong data-testid="summary-total-days" id="summary-total-days">0일</strong>
</div>
</div>

<div class="meta-grid meta-grid-secondary">
<div class="meta-value-card accent-card plan-card" title="κΈ°κ°„(일수) κ°€μ€‘μΉ˜κ°€ 반영된 ν”„λ‘œμ νŠΈ 전체 κ³„νš μ§„μ²™λ₯ μž…λ‹ˆλ‹€.">
<div class="meta-value-card accent-card plan-card" title="κΈ°κ°„(일수) κ°€μ€‘μΉ˜κ°€ 반영된 ν”„λ‘œμ νŠΈ 전체 κ³„νš μ§„μ²™λ₯ μž…λ‹ˆλ‹€." tabindex="0">
<span class="meta-label">κ³„νšμ§„μ²™λ₯ (λˆ„μ )</span>
<strong data-testid="summary-planned-progress" id="summary-planned-progress">0.00%</strong>
</div>
<div class="meta-value-card accent-card actual-card" title="κΈ°κ°„(일수) κ°€μ€‘μΉ˜κ°€ 반영된 ν”„λ‘œμ νŠΈ 전체 싀적 μ§„μ²™λ₯ μž…λ‹ˆλ‹€.">
<div class="meta-value-card accent-card actual-card" title="κΈ°κ°„(일수) κ°€μ€‘μΉ˜κ°€ 반영된 ν”„λ‘œμ νŠΈ 전체 싀적 μ§„μ²™λ₯ μž…λ‹ˆλ‹€." tabindex="0">
Comment on lines +31 to +42

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚑ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- candidate files ---'
git ls-files | rg '(^|/)(index\.html|app\.js|ARCHITECTURE\.md|AGENTS\.md|\.jules/)' || true

printf '%s\n' '--- index.html: relevant region ---'
nl -ba index.html | sed -n '1,75p'

printf '%s\n' '--- app.js: progress badge region ---'
nl -ba app.js | sed -n '950,1035p'

printf '%s\n' '--- related identifiers and render paths ---'
rg -n -C 3 'summary-total-days|summary-planned-progress|summary-actual-progress|progressState|description|renderAll\(' index.html app.js styles.css

printf '%s\n' '--- repository guidance ---'
for f in ARCHITECTURE.md AGENTS.md; do
  if [ -f "$f" ]; then
    printf '%s\n' "--- $f ---"
    sed -n '1,220p' "$f"
  fi
done
if [ -d .jules ]; then
  find .jules -maxdepth 2 -type f -print
fi

Repository: ContextualWisdomLab/scopeweave

Length of output: 16304


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- index.html: structure and labels ---'
awk 'NR >= 1 && NR <= 90 { printf "%4d %s\n", NR, $0 }' index.html

printf '%s\n' '--- app.js: status rendering and table context ---'
awk 'NR >= 680 && NR <= 755 { printf "%4d %s\n", NR, $0 }' app.js
awk 'NR >= 970 && NR <= 1010 { printf "%4d %s\n", NR, $0 }' app.js

printf '%s\n' '--- progress-state definitions and consumers ---'
awk 'NR >= 1415 && NR <= 1485 { printf "%4d %s\n", NR, $0 }' app.js
rg -n -C 4 'createStatusCellContent|status-badge|meta-value-card|summary-total-days|summary-planned-progress|summary-actual-progress' styles.css tests .jules 2>/dev/null || true

printf '%s\n' '--- relevant learning documents ---'
for f in .jules/*.md; do
  printf '%s\n' "--- $f ---"
  rg -n -C 2 'accessib|aria|focus|semantic|role|badge|summary|static|renderAll' "$f" || true
done

printf '%s\n' '--- accessibility-related tests ---'
rg -n -i -C 3 'accessib|aria|tabindex|role|keyboard|focus|status-badge|meta-value-card' tests 2>/dev/null || true

Repository: ContextualWisdomLab/scopeweave

Length of output: 50386


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- focused learning entry ---'
awk 'NR >= 100 && NR <= 125 { printf "%4d %s\n", NR, $0 }' .jules/palette.md

printf '%s\n' '--- targeted E2E accessibility assertions ---'
awk 'NR >= 1288 && NR <= 1335 { printf "%4d %s\n", NR, $0 }' tests/e2e/scopeweave.spec.js
awk 'NR >= 660 && NR <= 690 { printf "%4d %s\n", NR, $0 }' tests/e2e/scopeweave.spec.js

printf '%s\n' '--- focused source diff, if available ---'
git diff --unified=8 -- index.html app.js | sed -n '1,220p'

printf '%s\n' '--- deterministic attribute/label checks ---'
python3 - <<'PY'
from html.parser import HTMLParser
from pathlib import Path

class Parser(HTMLParser):
    def __init__(self):
        super().__init__()
        self.stack = []
        self.cards = []
        self.elements = []
    def handle_starttag(self, tag, attrs):
        attrs = dict(attrs)
        item = (tag, attrs, self.getpos()[0])
        self.elements.append(item)
        if 'tabindex' in attrs or 'tabIndex' in attrs:
            self.stack.append(item)
        if 'meta-value-card' in attrs.get('class', '').split():
            self.cards.append(item)

p = Parser()
p.feed(Path('index.html').read_text())
print('cards:')
for tag, attrs, line in p.cards:
    print({'line': line, 'tag': tag, 'tabindex': attrs.get('tabindex'),
           'role': attrs.get('role'), 'title': attrs.get('title'),
           'aria-label': attrs.get('aria-label'),
           'aria-labelledby': attrs.get('aria-labelledby'),
           'aria-describedby': attrs.get('aria-describedby')})
print('focusable static elements in index.html:')
for tag, attrs, line in p.elements:
    if 'tabindex' in attrs or 'tabIndex' in attrs:
        print({'line': line, 'tag': tag, 'tabindex': attrs.get('tabindex'),
               'role': attrs.get('role'), 'title': attrs.get('title'),
               'aria-label': attrs.get('aria-label'),
               'aria-labelledby': attrs.get('aria-labelledby'),
               'aria-describedby': attrs.get('aria-describedby')})
PY

Repository: ContextualWisdomLab/scopeweave

Length of output: 9206


🌐 Web query:

MDN tabindex non-interactive elements ARIA role focusable element title aria-describedby screen reader

πŸ’‘ Result:

To ensure accessibility, developers should prioritize semantic HTML and follow established patterns for focus management. Tabindex and Interactive Elements The tabindex attribute allows authors to control the focusability of elements [1][2]. However, developers should avoid using tabindex to make non-interactive elements interactive [1][3]. Instead, use native interactive elements like button, a, or input, which have built-in keyboard support and ARIA semantics [1][2][3]. If a non-interactive element must be used for custom functionality, it should be made focusable by setting tabindex="0" [2][4][5]. Positive values for tabindex (e.g., tabindex="1") must be avoided, as they disrupt the logical navigation order [1][2][4]. A value of -1 makes an element focusable programmatically via scripting (e.g., using element.focus) but removes it from the sequential keyboard navigation flow [2][4][5]. ARIA Roles and Focus When using ARIA roles (such as role="button") on non-interactive elements, the element does not automatically gain keyboard interactivity or focus management [6][7]. Developers must manually implement the necessary event listeners for click and keydown events (including Enter and Space key support) and ensure the element is focusable via tabindex [6]. Browser support for ARIA roles is ignored on naturally focusable elements or elements with tabindex defined [8]. Aria-describedby and Screen Readers The aria-describedby attribute provides a way to associate additional text with an element, which is then communicated to screen reader users [9][10]. It is used to link an element to one or more descriptive elements by their IDs [9][10]. Unlike the title attribute, which may have limited or inconsistent support, aria-describedby is widely supported and intended for providing context or descriptions [9][11][10]. It is appropriate for plain text descriptions; for more complex or structured content, aria-details is recommended [9][10]. If both aria-describedby and aria-description are present, aria-describedby takes precedence [10].

Citations:


🌐 Web query:

WAI-ARIA generic role aria-label prohibited accessible name focusable generic span role status static status badge

πŸ’‘ Result:

The WAI-ARIA specification explicitly prohibits the use of aria-label and aria-labelledby on elements with the generic role [1][2][3]. The generic role is intended to be used by user agents for containers that have no specific semantic meaning and should not be used by web authors [1][4]. Applying an accessible name (via aria-label or aria-labelledby) to a generic element is a violation of the ARIA specification [5][6]. The prohibition exists because the generic role does not have a semantic concept of a name [5]. When an author applies these attributes to a generic element, behavior across screen readers is inconsistent [5][7][6]. Some screen readers (such as VoiceOver) may announce the label [5][7][6], while others (such as NVDA or JAWS) ignore it completely [5][7], leading to unpredictable and non-standard accessibility experiences [8][6]. Regarding focusability: A standard span or div with a generic role is not focusable by default. If such an element is made focusable (e.g., via tabindex="0"), it should typically be given a specific, meaningful ARIA role (such as button or checkbox) rather than remaining generic, as users expect focusable elements to have a clear role and purpose [8]. There are narrow exceptions where an element's role may change, allowing for a label: 1. When a section element is given an aria-label, its role is implicitly elevated from generic to region, which is a named landmark [5][7]. 2. When a div has the popover attribute, its role is elevated to group, which is a role that supports naming [5][7]. Unless the element's role is programmatically changed to one that supports naming, authors should avoid attempting to label generic containers [5][8]. If an accessible name is required, the best practice is to use an element that inherently supports that role (e.g., using a button for an interactive action) [5][8].

Citations:


🌐 Web query:

W3C ARIA role status definition live region status badge role status accessibility

πŸ’‘ Result:

The ARIA role status is a defined landmark role used to create an assertive or polite live regionβ€”specifically for advisory informationβ€”that does not require immediate user interruption [1][2]. Definition and Purpose The status role is intended for content that provides advisory information regarding the state of an application or user activity [1][3]. It is not appropriate for critical errors or alerts that require immediate, interrupting attention; for those, the alert role is preferred [1][2]. Common use cases include success confirmations, loading messages, or updates to a shopping cart [3][2]. Key Technical Characteristics When an element is assigned role="status", it carries the following implicit ARIA properties: - aria-live="polite": This ensures that assistive technologies (AT), such as screen readers, announce the update only when the user is idle, rather than interrupting their current task [1][3][4]. - aria-atomic="true": This informs the AT that it should present the entire contents of the status container when an update occurs, rather than just the changed portion [1][3]. Best Practices for Accessibility - Implementation: To ensure reliability across various browsers and assistive technologies, it is often recommended to add an explicit aria-atomic="true" attribute, even though it is implicit, to ensure consistent behavior [3][5]. Similarly, some developers add redundant aria-live="polite" attributes to maximize compatibility [4]. - Container Presence: For the best results, the element designated as the status region should be present in the Document Object Model (DOM) when the page loads, and then updated dynamically with content [3][4][6]. - Content Context: Because status is often atomic, developers should ensure the container includes enough context so that the announcement is meaningful on its own [3]. If the status message relies on visual icons, ensure equivalent text (such as alt text or aria-label) is included within the status container [3]. - Avoid Focus: The status role is designed to inform users of changes without requiring them to move their focus; do not manually move focus to a status region [1].

Citations:


🌐 Web query:

WAI ARIA Authoring Practices informational card group aria-labelledby aria-describedby noninteractive focusable

πŸ’‘ Result:

There is no single, globally defined WAI-ARIA "card group" pattern [1]. Instead, accessibility for card groups is achieved by applying WAI-ARIA and HTML principles according to the specific interaction design (e.g., whether the group is static or interactive) [2][1]. Accessible Naming and Description To ensure assistive technologies identify and distinguish cards within a group: - Accessible Name: Use aria-labelledby to link the card to its primary heading (e.g., the card title) [3][4]. If no visible title exists, aria-label may be used as a fallback to provide a brief, descriptive name [2][3][4]. - Accessible Description: Use aria-describedby to associate the card with additional supporting information, such as body text or secondary details [2][5]. This provides context beyond the primary name [2]. Focusable vs. Non-interactive The interactivity of a card group determines its keyboard requirements: - Non-interactive Card Groups: If cards are primarily for information display, they generally do not need to be focusable [6]. Keyboard users navigate through the content naturally using standard page navigation (e.g., Tab key to reach links/buttons within cards) [6]. - Interactive/Selectable Card Groups: When cards act as components (e.g., selectable options or triggerable actions), they may be made focusable [7][5]. If a group contains multiple interactive elements, the WAI-ARIA Grid pattern or a roving tabindex approach is often used to manage focus, allowing users to navigate between cards using arrow keys while maintaining only one card in the page tab sequence [8][9][10]. Best Practices - Semantic HTML: Whenever possible, use native HTML elements (e.g., list items in an unordered list) to establish the group structure [5]. - Focus Management: If you choose to make cards focusable, ensure the focus indicator is clearly visible and that keyboard behavior (e.g., arrow key navigation) is consistent with familiar UI patterns [10][6]. - Avoid Redundancy: Do not use aria-labelledby or aria-describedby if the same information is already correctly identified by semantic structures (e.g., headings) that screen readers already announce [4]. Top results: [2][3][8][10][7]

Citations:


포컀슀 κ°€λŠ₯ν•œ λΉ„λŒ€ν™”ν˜• μš”μ†Œμ— μ ‘κ·Όμ„± 의미λ₯Ό λΆ€μ—¬ν•˜μ„Έμš”.

ν˜„μž¬ div와 span은 tabindex="0"이 μžˆμ–΄λ„ generic μš”μ†Œλ‘œ 남아 μžˆμŠ΅λ‹ˆλ‹€. span의 aria-label은 μΌκ΄€λ˜κ²Œ λ…ΈμΆœλ˜μ§€ μ•Šμ„ 수 μžˆμŠ΅λ‹ˆλ‹€.

  • index.html:31-42: 포컀슀λ₯Ό μœ μ§€ν•˜λ©΄ 이름을 μ§€μ›ν•˜λŠ” μ μ ˆν•œ 역할을 μΆ”κ°€ν•˜κ³ , ν‘œμ‹œ λ ˆμ΄λΈ”κ³Ό 동적 값을 aria-labelledby둜 μ—°κ²°ν•˜μ„Έμš”. μ„€λͺ…은 aria-describedby둜 μ—°κ²°ν•˜μ„Έμš”.
  • app.js:999-1002: μƒνƒœ 배지에도 λ™μž‘μ— λ§žλŠ” λΉ„-live μ—­ν• κ³Ό μ„€λͺ… 연결을 μΆ”κ°€ν•˜μ„Έμš”. 정적 ν‘œ μ…€μ—λŠ” 동적 μ•Œλ¦Όμ„ λ§Œλ“œλŠ” role="status"λ₯Ό μ‚¬μš©ν•˜μ§€ λ§ˆμ„Έμš”.
πŸ“ Affects 2 files
  • index.html#L31-L42 (this comment)
  • app.js#L999-L1002
πŸ€– 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 `@index.html` around lines 31 - 42, Update the focusable summary cards in
index.html:31-42 to use an appropriate non-generic role, assign IDs to their
visible labels and descriptions, and connect each card with aria-labelledby and
aria-describedby while preserving tabindex. In app.js:999-1002, update the
status badge with an appropriate non-live role and description reference; do not
use role="status" for static table cells.

<span class="meta-label">싀적진척λ₯ (λˆ„μ )</span>
<strong data-testid="summary-actual-progress" id="summary-actual-progress">0.00%</strong>
</div>
Expand Down
4 changes: 3 additions & 1 deletion styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,9 @@ button {
.secondary-button:focus-visible,
input:focus-visible,
select:focus-visible,
.gantt-bar:focus-visible {
.gantt-bar:focus-visible,
.meta-value-card:focus-visible,
.status-badge:focus-visible {
outline: 3px solid rgba(37, 99, 235, 0.5);
outline-offset: 2px;
}
Expand Down
Loading