π¨ Palette: λΉμνΈμμ© μμμ ν΄ν μ κ·Όμ± κ°μ - #481
π¨ Palette: λΉμνΈμμ© μμμ ν΄ν μ κ·Όμ± κ°μ #481seonghobae wants to merge 5 commits into
Conversation
λΉμνΈμμ© μμμΈ `div`(`.meta-value-card`)μ λ§μ°μ€ νΈλ² μμλ§ λ³΄μ΄λ ν΄ν(`title` μμ±)μ΄ μ 곡λκ³ μμ΄ μ€ν¬λ¦° 리λμ ν€λ³΄λ μ¬μ©μλ ν΄λΉ μ 보λ₯Ό μΈμ§ν μ μμμ΅λλ€. μ΄μ `tabindex="0"`κ³Ό `role="note"`λ₯Ό μΆκ°νμ¬ ν¬μ»€μ€λ₯Ό λ°μ μ μκ² νκ³ μ€ν¬λ¦° 리λκ° μ¬λ°λ₯΄κ² λ§₯λ½μ μ½μ μ μλλ‘ νμμΌλ©°, `.meta-value-card:focus-visible`λ₯Ό μΆκ°νμ¬ ν€λ³΄λ νμ μ μκ°μ μΈ ν¬μ»€μ€ μΈλμΌμ΄ν°λ₯Ό μ 곡νλλ‘ κ°μ νμ΅λλ€.
|
π Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a π emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
π WalkthroughWalkthroughμμ½ μΉ΄λκ° ν€λ³΄λ ν¬μ»€μ€λ₯Ό λ°μ μ μλλ‘ Changesμ κ·Όμ± κ°μ
Estimated code review effort: 1 (λ§€μ° κ°λ¨) | ~3λΆ π₯ Pre-merge checks | β 5β Passed checks (5 passed)
β¨ Finishing Touchesπ§ͺ Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
λΉμνΈμμ© μμμΈ `div`(`.meta-value-card`)μ λ§μ°μ€ νΈλ² μμλ§ λ³΄μ΄λ ν΄ν(`title` μμ±)μ΄ μ 곡λκ³ μμ΄ μ€ν¬λ¦° 리λμ ν€λ³΄λ μ¬μ©μλ ν΄λΉ μ 보λ₯Ό μΈμ§ν μ μμμ΅λλ€. μ΄μ `tabindex="0"`κ³Ό `role="note"`λ₯Ό μΆκ°νμ¬ ν¬μ»€μ€λ₯Ό λ°μ μ μκ² νκ³ μ€ν¬λ¦° 리λκ° μ¬λ°λ₯΄κ² λ§₯λ½μ μ½μ μ μλλ‘ νμμΌλ©°, `.meta-value-card:focus-visible`λ₯Ό μΆκ°νμ¬ ν€λ³΄λ νμ μ μκ°μ μΈ ν¬μ»€μ€ μΈλμΌμ΄ν°λ₯Ό μ 곡νλλ‘ κ°μ νμ΅λλ€.
There was a problem hiding this comment.
Actionable comments posted: 1
π§Ή Nitpick comments (2)
index.html (1)
31-42: π Maintainability & Code Quality | π΅ Trivial | β‘ Quick winμΆκ°λ μ κ·Όμ± μμ±μ E2E ν μ€νΈλ‘ κ²μ¦νμΈμ.
tests/e2e/scopeweave.spec.js:1293-1335λ μΉ΄λμtitleλ§ κ²μ¦ν©λλ€.tabindex="0",role="note", μ€μ ν€λ³΄λ ν¬μ»€μ€ μνλ κ²μ¦νμ§ μμ΅λλ€. μΈ μΉ΄λμ μμ±κ³Ό ν€λ³΄λ ν¬μ»€μ€λ₯Ό ν μ€νΈμ μΆκ°νμΈμ.π€ 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 E2E coverage around the existing card assertions in scopeweave.spec.js to verify all three cards have tabindex="0" and role="note", in addition to their titles. Add keyboard-focus checks that move focus to each card and confirm it becomes the active element, covering the total-days, planned-progress, and actual-progress cards..jules/palette.md (1)
118-120: π Maintainability & Code Quality | π΅ Trivial | β‘ Quick winμ΄ μ§μΉ¨μ λͺ¨λ λΉμνΈμμ© μμμ κ°μ νμ§ λ§μΈμ.
tabindex="0"μ μ μ μ½ν μΈ μλ ν μ λ₯μ₯μ μΆκ°ν©λλ€.role="note"λ tooltip μν μ΄ μλλλ€. μ΄ μ‘°ν©μ λΆνμν ν μ λ₯μ₯μ λ§λ€κ³titleμ μ νμ ν΄κ²°νμ§ λͺ»ν μ μμ΅λλ€. WAI-ARIAλnoteλ₯Ό λΆκ° μ 보μ μ¬μ©νκ³ , tooltipμ ν¬μ»€μ€ λμκ³Όaria-describedbyλ₯Ό μ°κ²°νλ λ°©μμΌλ‘ μ μν©λλ€. (w3.org)μ§μΉ¨μ 쑰건λΆλ‘ λ³κ²½νμΈμ. ν€λ³΄λ ν¬μ»€μ€κ° νμν μμμλ§
tabindex="0"μ μ¬μ©νμΈμ. μ€λͺ μ λͺ μμ μΈ DOM μ½ν μΈ μaria-describedbyλ‘ μ 곡νμΈμ. μ€μ tooltipμ΄ νμνλ©΄ ν¬μ»€μ€μ hoverλ₯Ό λͺ¨λ μ²λ¦¬νλ tooltip ν¨ν΄μ μ¬μ©νμΈμ.As per coding guidelines, static hosting νκ²½μμλ λΈλΌμ°μ κΈ°λ³Έ APIλ₯Ό μ°μ νκ³ λΆνμν λ°νμ λμμ μΆκ°νμ§ μμμΌ ν©λλ€.
π€ 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 @.jules/palette.md around lines 118 - 120, Revise the βλΉμνΈμμ© μμμ ν΄ν μ κ·Όμ± κ°μ β guidance to be conditional rather than requiring tabindex="0" and role="note" for every non-interactive element. Recommend tabindex="0" only when keyboard focus is genuinely needed, use explicit DOM descriptions with aria-describedby, and reserve tooltip patterns for cases supporting both focus and hover; avoid implying unnecessary runtime behavior in static hosting environments.Source: Coding guidelines
π€ 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 `@index.html`:
- Around line 31-42: Replace the inaccessible title-only tooltips on all three
meta-value-card elements in index.html with explicit description elements
connected via aria-describedby, and implement showing and hiding those
descriptions on focus and mouse interaction while preserving the existing
meta-value-card:focus-visible styling. Update related E2E tests to verify
accessible descriptions and interactions. In .jules/palette.md lines 118-120,
apply tabindex="0" and role="note" only where the documented accessibility
behavior requires them; update the guidance to match the new pattern.
---
Nitpick comments:
In @.jules/palette.md:
- Around line 118-120: Revise the βλΉμνΈμμ© μμμ ν΄ν μ κ·Όμ± κ°μ β guidance to be
conditional rather than requiring tabindex="0" and role="note" for every
non-interactive element. Recommend tabindex="0" only when keyboard focus is
genuinely needed, use explicit DOM descriptions with aria-describedby, and
reserve tooltip patterns for cases supporting both focus and hover; avoid
implying unnecessary runtime behavior in static hosting environments.
In `@index.html`:
- Around line 31-42: Update the E2E coverage around the existing card assertions
in scopeweave.spec.js to verify all three cards have tabindex="0" and
role="note", in addition to their titles. Add keyboard-focus checks that move
focus to each card and confirm it becomes the active element, covering the
total-days, planned-progress, and actual-progress cards.
πͺ Autofix
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: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: f1b8e466-259f-445a-8481-7fda20d9280b
π Files selected for processing (3)
.jules/palette.mdindex.htmlstyles.css
| <div class="meta-value-card" title="νλ‘μ νΈμ μμ κΈ°κ°(μΌμ) ν©κ³μ λλ€." tabindex="0" role="note"> | ||
| <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" role="note"> | ||
| <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" role="note"> |
There was a problem hiding this comment.
π― Functional Correctness | π Major | ποΈ Heavy lift
π§© Analysis chain
π Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- index.html target and script order ---'
nl -ba index.html | sed -n '1,65p'
printf '%s\n' '--- palette learning ---'
nl -ba .jules/palette.md | sed -n '105,130p'
printf '%s\n' '--- related tooltip/focus/description patterns ---'
rg -n -i 'tooltip|aria-describedby|role="tooltip"|role="note"|focus-visible|tabindex="0"|meta-value-card|summary-total-days|summary-planned-progress' --glob '!node_modules/**' .Repository: ContextualWisdomLab/scopeweave
Length of output: 2761
π Web query:
W3C WAI ARIA APG tooltip role note title attribute keyboard focus aria-describedby tooltip guidance
π‘ Result:
To create an accessible tooltip, the W3C WAI ARIA Authoring Practices Guide (APG) provides specific guidance to ensure compatibility with screen readers and keyboard navigation [1][2]. Key requirements for an accessible tooltip include: Role and Association: Use role="tooltip" on the tooltip container element [1][3]. The triggering element must reference the tooltip container using the aria-describedby attribute [1][4]. Keyboard Focus: Tooltips do not receive focus themselves [1][3]. Focus remains on the triggering element while the tooltip is visible [1][5]. The tooltip must appear when the trigger receives focus and disappear when the trigger loses focus (onBlur) [1][6]. Dismissal: The Escape key should be implemented to dismiss the tooltip [1][3][7]. If triggered by hover, it should disappear on mouse-out [1][3]. To meet WCAG 1.4.13 requirements, tooltips must be dismissible without moving the pointer, and if triggered by hover, they should remain visible when the pointer moves from the trigger to the tooltip itself [3][7]. Avoid the title Attribute: You should avoid using the HTML title attribute for tooltips [8][5]. It is not reliably accessible, cannot be styled, is often not triggered by keyboard focus, and is frequently ignored by screen readers [3][8]. Implementation Note: The tooltip pattern is considered a work in progress by the WAI, and developers are encouraged to follow the latest APG recommendations for specific keyboard and focus management [1]. For content that is complex or interactive, consider using a different pattern, such as a popover or modal dialog, as tooltips are intended for short, non-interactive contextual information [1][3].
Citations:
- 1: https://www.w3.org/WAI/ARIA/apg/patterns/tooltip/
- 2: https://www.w3.org/WAI/ARIA/apg/
- 3: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/tooltip_role
- 4: https://www.w3.org/TR/WCAG-TECHS/ARIA1.html
- 5: https://engineered.at/articles/psa-stop-using-the-title-attribute-as-tooltip
- 6: https://www.w3.org/wiki/PF/ARIA/BestPractices/Tooltips
- 7: https://www.w3.org/WAI/WCAG21/Techniques/client-side-script/SCR39.html
- 8: https://a11ypath.com/patterns/tooltip/
π Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- index.html target ---'
awk '{printf "%5d %s\n", NR, $0}' index.html | sed -n '25,55p'
printf '%s\n' '--- focus styles ---'
awk '{printf "%5d %s\n", NR, $0}' styles.css | sed -n '110,180p;340,375p'
printf '%s\n' '--- tooltip-related test ---'
awk '{printf "%5d %s\n", NR, $0}' tests/e2e/scopeweave.spec.js | sed -n '1280,1325p'
printf '%s\n' '--- all title attributes and note roles ---'
rg -n 'title=|role="note"|aria-describedby|role="tooltip"' --glob '*.html' --glob '*.js' --glob '*.css' --glob '*.md' .Repository: ContextualWisdomLab/scopeweave
Length of output: 7917
π Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- relevant architecture and learning guidance files ---'
git ls-files 'ARCHITECTURE.md' 'AGENTS.md' '.jules/*.md'
printf '%s\n' '--- palette file size and surrounding guidance ---'
wc -l .jules/palette.md
awk '{printf "%5d %s\n", NR, $0}' .jules/palette.md | sed -n '105,125p'Repository: ContextualWisdomLab/scopeweave
Length of output: 2726
π Script executed:
#!/bin/bash
set -eu
python3 - <<'PY'
from html.parser import HTMLParser
from pathlib import Path
class CardParser(HTMLParser):
def __init__(self):
super().__init__()
self.cards = []
self.in_card = False
self.current = None
def handle_starttag(self, tag, attrs):
attrs = dict(attrs)
classes = set((attrs.get("class") or "").split())
if tag == "div" and "meta-value-card" in classes:
self.in_card = True
self.current = {"attrs": attrs}
self.cards.append(self.current)
def handle_endtag(self, tag):
if tag == "div" and self.in_card:
self.in_card = False
self.current = None
parser = CardParser()
parser.feed(Path("index.html").read_text())
print(f"meta-value-card count: {len(parser.cards)}")
for i, card in enumerate(parser.cards, 1):
attrs = card["attrs"]
print(i, {k: attrs.get(k) for k in ("title", "tabindex", "role", "aria-describedby", "id")})
palette = Path(".jules/palette.md").read_text()
print("palette forces tabindex=0:", 'νμ tabindex="0"' in palette)
print("palette forces role=note:", 'role="note"' in palette)
print("focus-visible selector exists:",
".meta-value-card:focus-visible" in Path("styles.css").read_text())
PYRepository: ContextualWisdomLab/scopeweave
Length of output: 643
titleμ μ κ·Ό κ°λ₯ν ν΄νμΌλ‘ μ¬μ©νμ§ λ§μΈμ.
μΈ κ°μ .meta-value-card λͺ¨λ titleκ³Ό role="note"λ§ μ¬μ©νλ©° aria-describedby μ°κ²°μ΄ μμ΅λλ€. λͺ
μμ μ€λͺ
μμλ₯Ό μΆκ°νκ³ , ν¬μ»€μ€Β·λ§μ°μ€ λμκ³Ό μ€λͺ
νμλ₯Ό ꡬννμΈμ. κΈ°μ‘΄ .meta-value-card:focus-visible μ€νμΌμ μ μ§νμΈμ. .jules/palette.mdμ tabindex="0" λ° role="note" μ μ©λ νμν κ²½μ°λ‘ μ ννμΈμ. κ΄λ ¨ E2E ν
μ€νΈμμ μ κ·Ό κ°λ₯ν μ€λͺ
κ³Ό λμμ κ²μ¦νμΈμ.
π Affects 2 files
index.html#L31-L42(this comment).jules/palette.md#L118-L120
π€ 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, Replace the inaccessible title-only
tooltips on all three meta-value-card elements in index.html with explicit
description elements connected via aria-describedby, and implement showing and
hiding those descriptions on focus and mouse interaction while preserving the
existing meta-value-card:focus-visible styling. Update related E2E tests to
verify accessible descriptions and interactions. In .jules/palette.md lines
118-120, apply tabindex="0" and role="note" only where the documented
accessibility behavior requires them; update the guidance to match the new
pattern.
Source: Coding guidelines
λΉμνΈμμ© μμμΈ `div`(`.meta-value-card`)μ λ§μ°μ€ νΈλ² μμλ§ λ³΄μ΄λ ν΄ν(`title` μμ±)μ΄ μ 곡λκ³ μμ΄ μ€ν¬λ¦° 리λμ ν€λ³΄λ μ¬μ©μλ ν΄λΉ μ 보λ₯Ό μΈμ§ν μ μμμ΅λλ€. μ΄μ `tabindex="0"`κ³Ό `role="note"`λ₯Ό μΆκ°νμ¬ ν¬μ»€μ€λ₯Ό λ°μ μ μκ² νκ³ μ€ν¬λ¦° 리λκ° μ¬λ°λ₯΄κ² λ§₯λ½μ μ½μ μ μλλ‘ νμμΌλ©°, `.meta-value-card:focus-visible`λ₯Ό μΆκ°νμ¬ ν€λ³΄λ νμ μ μκ°μ μΈ ν¬μ»€μ€ μΈλμΌμ΄ν°λ₯Ό μ 곡νλλ‘ κ°μ νμ΅λλ€.
λΉμνΈμμ© μμμΈ `div`(`.meta-value-card`)μ λ§μ°μ€ νΈλ² μμλ§ λ³΄μ΄λ ν΄ν(`title` μμ±)μ΄ μ 곡λκ³ μμ΄ μ€ν¬λ¦° 리λμ ν€λ³΄λ μ¬μ©μλ ν΄λΉ μ 보λ₯Ό μΈμ§ν μ μμμ΅λλ€. μ΄μ `tabindex="0"`κ³Ό `role="note"`λ₯Ό μΆκ°νμ¬ ν¬μ»€μ€λ₯Ό λ°μ μ μκ² νκ³ μ€ν¬λ¦° 리λκ° μ¬λ°λ₯΄κ² λ§₯λ½μ μ½μ μ μλλ‘ νμμΌλ©°, `.meta-value-card:focus-visible`λ₯Ό μΆκ°νμ¬ ν€λ³΄λ νμ μ μκ°μ μΈ ν¬μ»€μ€ μΈλμΌμ΄ν°λ₯Ό μ 곡νλλ‘ κ°μ νμ΅λλ€.
λΉμνΈμμ© μμμΈ `div`(`.meta-value-card`)μ λ§μ°μ€ νΈλ² μμλ§ λ³΄μ΄λ ν΄ν(`title` μμ±)μ΄ μ 곡λκ³ μμ΄ μ€ν¬λ¦° 리λμ ν€λ³΄λ μ¬μ©μλ ν΄λΉ μ 보λ₯Ό μΈμ§ν μ μμμ΅λλ€. μ΄μ `tabindex="0"`κ³Ό `role="note"`λ₯Ό μΆκ°νμ¬ ν¬μ»€μ€λ₯Ό λ°μ μ μκ² νκ³ μ€ν¬λ¦° 리λκ° μ¬λ°λ₯΄κ² λ§₯λ½μ μ½μ μ μλλ‘ νμμΌλ©°, `.meta-value-card:focus-visible`λ₯Ό μΆκ°νμ¬ ν€λ³΄λ νμ μ μκ°μ μΈ ν¬μ»€μ€ μΈλμΌμ΄ν°λ₯Ό μ 곡νλλ‘ κ°μ νμ΅λλ€.
|
Closing this duplicate accessibility approach because adding static cards to the sequential tab order is not equivalent to making their information accessible.
#445 and #485 propose the same pattern. A replacement should expose the explanatory text directly through visible or programmatically associated content without forcing every non-interactive metric into the tab sequence. |
Understood. Acknowledging that this approach is being closed as obsolete because adding |
π‘ 무μμ
index.htmlλ΄μ μλ λΉμνΈμμ© μμμΈ.meta-value-card3κ°μtabindex="0"κ³Όrole="note"μμ±μ μΆκ°νμ΅λλ€.styles.cssμ.meta-value-card:focus-visibleν΄λμ€λ₯Ό μΆκ°νμ¬ ν¬μ»€μ€ μνμΌ λ κΈ°μ‘΄ μΈν°λν°λΈ μμλ€κ³Ό λμΌν ν¬μ»€μ€ λ§μ΄ 보μ΄λλ‘ μ€νμΌμ μ μ©νμ΅λλ€.π― μ
.meta-value-cardμμλ€μ νλ‘μ νΈ μμ½ μ 보(μ: κ³νμ§μ²λ₯ , μ€μ μ§μ²λ₯ )μ ν¨κ»titleμμ±μ ν΅ν΄ ꡬ체μ μΈ μ€λͺ μ ν΄νμΌλ‘ μ 곡νκ³ μμ΅λλ€. νμ§λ§divμ κ°μ λΉμνΈμμ© μμλ κΈ°λ³Έμ μΌλ‘ ν¬μ»€μ€λ₯Ό λ°μ μ μκΈ° λλ¬Έμ ν€λ³΄λλ§μΌλ‘ νμνλ μ¬μ©μμ μ€ν¬λ¦° 리λ μ¬μ©μλ μ΄ μ μ©ν ν΄ν μ 보μ μ ν μ κ·Όν μ μλ μ κ·Όμ± λ¬Έμ κ° μμμ΅λλ€. μ΄λ₯Ό ν΄κ²°νκΈ° μν΄ ν¬μ»€μ€ κ°λ₯νκ² λ§λ€κ³ μλ―Έλ‘ μ μΈ μν μ λΆμ¬νμ΅λλ€.πΈ λ³κ²½ μ /ν
.meta-value-card)λ₯Ό 건λλ°λ©°, μ€ν¬λ¦° 리λλ ν΄λΉ μΉ΄λμtitleμ 보λ₯Ό μ½μ§ λͺ»νμ΅λλ€.noteμν κ³Ό ν¨κ» μΉ΄λμ μ€λͺ (title)μ΄ μ¬λ°λ₯΄κ² μ λ¬λ©λλ€.βΏ μ κ·Όμ±
λΉμνΈμμ© μμμ ν΄ν μ λ³΄κ° λ΄κ²¨ μμ λ μκ°μ₯μ μΈ(μ€ν¬λ¦° 리λ μ¬μ©μ) λ° μ΄λμ₯μ μΈ(ν€λ³΄λ μ μ© μ¬μ©μ)λ λμΌν μ 보λ₯Ό νλν μ μλλ‘ ν€λ³΄λ μ΄μ μ§μ(
tabindex="0"), μλ―Έλ‘ μ μ 보 μ 곡(role="note"), κ·Έλ¦¬κ³ κ°μμ μΈ μ΄μ νμ(:focus-visible)λ₯Ό μΆκ°νμ¬ μ κ·Όμ±μ λν κ°μ νμ΅λλ€.PR created automatically by Jules for task 8476983301548473241 started by @seonghobae
Summary by CodeRabbit