Conversation
- web/mobile 加入明暗主題切換(ThemeProvider/ThemeContext/ThemeToggle),globals.css 與各元件配色跟著主題走 - mobile 人類圖 PDF 報告(個人/合圖/流日)內容修正,跟畫面(儲存前預覽/已儲存詳情頁)完全對齊: - 補齊類型策略/簽名/非自我主題等缺漏欄位、九大中心與定義通道改用中文標籤並標示啟用狀態 - 修正合圖行星表格配色語意、連結動力顏色改跟著明暗主題走、移除誤導性的原始整合代碼 - 修正流日 Body Graph 黑紅語意錯誤(改用「個人 vs 今日流日」而非 Personality/Design)、重建行星閘門對照表與流日影響分析卡片 - 三份報告共用的版面小工具(row/tags/section 等)合併成模組層級函式,避免各自維護造成內容drift - PDF 下載檔名改為「圖表種類-圖表名稱-下載時間.pdf」,透過 expo-file-system 複製成可讀檔名再分享 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThis PR adds light/dark theme support across web and mobile interfaces, replaces hardcoded colors with theme tokens, updates mobile composite and transit presentations, and passes theme state into personal, composite, and transit PDF generation. ChangesWeb theme and Human Design styling
Mobile theming and chart presentation
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant AppLayout
participant ThemeProvider
participant ThemeToggle
participant ChartScreen
participant ChartPdf
AppLayout->>ThemeProvider: initialize theme
ThemeToggle->>ThemeProvider: toggle theme
ThemeProvider->>ChartScreen: provide colors and mode
ChartScreen->>ChartPdf: download chart with mode
ChartPdf->>ChartPdf: build themed PDF HTML and SVG
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 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 |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (3)
mobile/components/NotificationsView.tsx (1)
19-23: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winShare themed styles and configuration across notification rows.
NotificationCardrebuilds the sameStyleSheetand type configuration once perFlatListitem. Compute them once inNotificationsViewand pass them into the card to avoid redundant allocations as the notification list grows.Proposed refactor
-function NotificationCard({ item }: { item: AppNotification }) { - const Colors = useThemeColors() - const s = useMemo(() => createStyles(Colors), [Colors]) - const typeCfg = useMemo(() => createTypeCfg(Colors), [Colors]) +function NotificationCard({ + item, + s, + typeCfg, +}: { + item: AppNotification + s: ReturnType<typeof createStyles> + typeCfg: ReturnType<typeof createTypeCfg> +}) { const cfg = typeCfg[item.type] ?? typeCfg.announcementexport default function NotificationsView() { const Colors = useThemeColors() const s = useMemo(() => createStyles(Colors), [Colors]) + const typeCfg = useMemo(() => createTypeCfg(Colors), [Colors])- renderItem={({ item }) => <NotificationCard item={item} />} + renderItem={({ item }) => <NotificationCard item={item} s={s} typeCfg={typeCfg} />}🤖 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 `@mobile/components/NotificationsView.tsx` around lines 19 - 23, Move the useThemeColors, createStyles, and createTypeCfg computations from NotificationCard into NotificationsView so each list render creates them once. Pass the resulting styles and type configuration into NotificationCard, and use those props when selecting the notification config and rendering the row while preserving existing themed behavior.components/ThemeProvider.tsx (1)
17-24: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDocument the new theme-switching behavior.
components/ThemeProvider.tsx#L17-L24: add a complete comment covering the state transition, DOMdata-themeupdate, and cookie persistence contract.components/ThemeToggle.tsx#L5-L15: add a component-level comment covering the theme-state-to-icon, action-label, andaria-pressedmapping.As per coding guidelines: “Each functionality should be implemented as an independent, reusable function with complete comments including calculation principles.”
🤖 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 `@components/ThemeProvider.tsx` around lines 17 - 24, Document both theme-switching sites: in components/ThemeProvider.tsx lines 17-24, add a complete comment for toggleTheme covering the dark/light state transition, document.documentElement data-theme update, and one-year SameSite=Lax cookie persistence; in components/ThemeToggle.tsx lines 5-15, add a component-level comment describing how theme state maps to the displayed icon, action label, and aria-pressed value.Source: Coding guidelines
mobile/app/chart/[id].tsx (1)
55-84: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExtract a shared
ActionButtonintoChartPrimitives.tsx. Both chart screens independently define anActionButtoncomponent plus identicalactionBtn*style keys inside their owncreateStyles(Colors), right alongside the same PR's centralization ofSectionCard/Row/TagintoChartPrimitives.tsx.
mobile/app/chart/[id].tsx#L55-L84: move thisActionButton(and itsactionBtn*style keys) intoChartPrimitives.tsx, exporting a shared component parameterized by the existing props.mobile/app/chart/preview.tsx#L32-L65: replace this variant (which addsnotLoggedInLabel/isLoggedIn) with the shared component, extending its props to support the not-logged-in label case, and drop the duplicatedactionBtn*keys from its localcreateStyles.🤖 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 `@mobile/app/chart/`[id].tsx around lines 55 - 84, The duplicated ActionButton implementations should be centralized in ChartPrimitives.tsx. At mobile/app/chart/[id].tsx lines 55-84, move the component and its actionBtn* styles into the shared module and update the screen to use the exported component; at mobile/app/chart/preview.tsx lines 32-65, replace the local variant with that shared component, extending its props to preserve notLoggedInLabel/isLoggedIn behavior, and remove the duplicated actionBtn* styles from createStyles.
🤖 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/globals.css`:
- Around line 33-35: Update the font token declarations in the global styles to
avoid self-references: define separate --next-font-serif, --next-font-sans, and
--next-font-mono fallback variables, then map --font-serif, --font-sans, and
--font-mono to the corresponding --next-font-* variables. Preserve the existing
Georgia, system-ui, and ui-monospace fallbacks.
In `@components/ui/calendar.tsx`:
- Around line 154-159: Update the calendar class configuration around day_button
and selected so selected days retain the light paper text color on hover; ensure
the selected-state hover text utility takes precedence over day_button’s
hover:text-[var(--ink-on-tan)] without changing unselected-day hover styling.
In `@mobile/components/chart/CompositeInfo.tsx`:
- Around line 111-143: Update the 行星閘門對照 group-header row in the CompositeInfo
component so each planetGroupLabel for nameA and nameB spans or occupies the
combined width of its two underlying planetGateColSm columns, matching the
four-column data and sub-header rows. Preserve the leading empty planet column
and keep the two person groups centered over their respective 意識/潛意識 pairs.
In `@mobile/constants/tokens.ts`:
- Around line 2-4: Update the new semantic tokens transitWarmText, successText,
and successBg in the mobile color palette to use the matching hex values from
app/globals.css, or explicitly document them as mobile-only if no web
equivalents are intended; keep shared palette tokens synchronized across light
and dark themes.
In `@mobile/contexts/ThemeContext.tsx`:
- Around line 23-35: Update the initialization effect and toggleTheme callback
in ThemeContext so an asynchronously resolved stored theme cannot overwrite a
mode changed locally by toggleTheme. Track whether a local toggle has occurred,
and apply the restored value only if no toggle happened before the storage read
completed; preserve the existing light/dark validation and persistence behavior.
In `@mobile/lib/chartPdf.ts`:
- Around line 34-49: Update the dimBg assignment in pdfPalette to use c.gateBg
for both dark and light themes, replacing the mode-specific darkColors.altRowBg
fallback while preserving the existing palette structure.
---
Nitpick comments:
In `@components/ThemeProvider.tsx`:
- Around line 17-24: Document both theme-switching sites: in
components/ThemeProvider.tsx lines 17-24, add a complete comment for toggleTheme
covering the dark/light state transition, document.documentElement data-theme
update, and one-year SameSite=Lax cookie persistence; in
components/ThemeToggle.tsx lines 5-15, add a component-level comment describing
how theme state maps to the displayed icon, action label, and aria-pressed
value.
In `@mobile/app/chart/`[id].tsx:
- Around line 55-84: The duplicated ActionButton implementations should be
centralized in ChartPrimitives.tsx. At mobile/app/chart/[id].tsx lines 55-84,
move the component and its actionBtn* styles into the shared module and update
the screen to use the exported component; at mobile/app/chart/preview.tsx lines
32-65, replace the local variant with that shared component, extending its props
to preserve notLoggedInLabel/isLoggedIn behavior, and remove the duplicated
actionBtn* styles from createStyles.
In `@mobile/components/NotificationsView.tsx`:
- Around line 19-23: Move the useThemeColors, createStyles, and createTypeCfg
computations from NotificationCard into NotificationsView so each list render
creates them once. Pass the resulting styles and type configuration into
NotificationCard, and use those props when selecting the notification config and
rendering the row while preserving existing themed behavior.
🪄 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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 9ce4ca1e-81d3-48dd-b208-9696efa7fd39
⛔ Files ignored due to path filters (1)
mobile/assets/store/icon-512.pngis excluded by!**/*.png
📒 Files selected for processing (70)
app/account/AccountClient.tsxapp/aura-flow/page.tsxapp/globals.cssapp/layout.tsxcomponents/Navbar.tsxcomponents/SelfMap.tsxcomponents/ThemeProvider.tsxcomponents/ThemeToggle.tsxcomponents/humanDesign/BirthProfileManager.tsxcomponents/humanDesign/BodyGraph.tsxcomponents/humanDesign/ChartView.tsxcomponents/humanDesign/CompositeTab.tsxcomponents/humanDesign/CompositeView.tsxcomponents/humanDesign/DetailDrawer.tsxcomponents/humanDesign/LocationPicker.tsxcomponents/humanDesign/TimezonePickerModal.tsxcomponents/humanDesign/TransitView.tsxcomponents/ui/calendar.tsxcomponents/ui/select.tsxlib/clerkAppearance.tslib/downloadChart.tslib/theme.tsmobile/app/(auth)/sign-in.tsxmobile/app/(tabs)/_layout.tsxmobile/app/(tabs)/create.tsxmobile/app/(tabs)/index.tsxmobile/app/(tabs)/learn.tsxmobile/app/(tabs)/profile.tsxmobile/app/(tabs)/theme-toggle.tsxmobile/app/_layout.tsxmobile/app/chart/[id].tsxmobile/app/chart/preview.tsxmobile/app/learn/[topic].tsxmobile/components/AppliedProfileCard.tsxmobile/components/BirthDataForm.tsxmobile/components/BirthProfilePickerModal.tsxmobile/components/BirthProfileSheet.tsxmobile/components/ChartListView.tsxmobile/components/CitySearchField.tsxmobile/components/CompositeView.tsxmobile/components/DateTimePicker.tsxmobile/components/DetailBottomSheet.tsxmobile/components/ErrorBoundary.tsxmobile/components/InputModal.tsxmobile/components/NavBackHeader.tsxmobile/components/NotificationsView.tsxmobile/components/ScreenHeader.tsxmobile/components/StateViews.tsxmobile/components/SubTabBar.tsxmobile/components/TimezonePickerModal.tsxmobile/components/TransitView.tsxmobile/components/WheelPicker.tsxmobile/components/chart/ChartPrimitives.tsxmobile/components/chart/CompositeInfo.tsxmobile/components/chart/PersonalChartDetails.tsxmobile/components/chart/TransitAnalysis.tsxmobile/components/composite/compositeText.tsmobile/components/learn/CenterList.tsxmobile/components/learn/ChannelList.tsxmobile/components/learn/CompositeLearn.tsxmobile/components/learn/GateList.tsxmobile/components/learn/SummaryList.tsxmobile/components/learn/TransitLearn.tsxmobile/components/learn/learnStyles.tsmobile/components/transit/GateChip.tsxmobile/components/transit/ImpactCard.tsxmobile/components/transit/icons.tsxmobile/constants/tokens.tsmobile/contexts/ThemeContext.tsxmobile/lib/chartPdf.ts
💤 Files with no reviewable changes (1)
- mobile/components/ScreenHeader.tsx
| --font-serif: var(--font-serif, "Georgia", serif); | ||
| --font-sans: var(--font-sans, system-ui, sans-serif); | ||
| --font-mono: var(--font-mono, ui-monospace, monospace); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C2 \
-e "variable:\\s*['\"]--font" \
-e "--font-(serif|sans|mono)" \
app/layout.tsx app/globals.cssRepository: Retsomm/SelfMap
Length of output: 7323
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== app/layout.tsx ==\n'
cat -n app/layout.tsx | sed -n '1,90p'
printf '\n== app/globals.css ==\n'
cat -n app/globals.css | sed -n '1,55p'Repository: Retsomm/SelfMap
Length of output: 5143
🏁 Script executed:
#!/bin/bash
set -euo pipefail
cat -n app/layout.tsx | sed -n '90,170p'Repository: Retsomm/SelfMap
Length of output: 2562
🌐 Web query:
CSS custom property self-referential var() fallback cycle invalid at computed-value time MDN
💡 Result:
In CSS, custom properties can form cyclic dependencies if they reference each other in a way that creates a loop (e.g., --a: var(--b); --b: var(--a);) [1][2]. When a cycle exists in the dependency graph, all custom properties involved in that cycle become invalid at computed-value time [1][3]. When a custom property is invalid at computed-value time, its computed value is set to the guaranteed-invalid value [1][3]. If another property or custom property attempts to reference this cyclically dependent variable without a valid fallback, that referencing property also becomes invalid at computed-value time [1][4]. Key points regarding this behavior include: Invalid at Computed-Value Time: CSS custom properties are not fully parsed until computed-value time [1]. Because cycles can only be detected after substitution, they are considered valid at parse time but become invalid at computed-value time [1]. Guaranteed-Invalid Value: This is a special internal value [1][5]. If a property's computed value is this guaranteed-invalid value, the property is treated as invalid [1][6]. For non-registered custom properties, this results in the property effectively having no usable value [1][3]. Fallback Substitution: If a var function references a custom property that has resolved to the guaranteed-invalid value, the function will attempt to use its fallback value (the second argument of var) [7][8]. If the fallback is also missing or invalid, the entire property becomes invalid at computed-value time [7][6]. Resulting Behavior: Once a property is invalid at computed-value time, it resets to its initial value (or inherited value, if applicable), effectively acting as if the property had been set to the unset keyword [4][9][3]. This mechanism ensures that cyclic dependencies do not cause infinite loops or application crashes, instead gracefully failing to a safe state [1][10].
Citations:
- 1: https://www.w3.org/TR/css-variables-1/
- 2: https://pawelgrzybek.com/til-invalid-css-cyclic-dependencies-and-a-valid-empty-fallback-value-using-a-bare-comma/
- 3: Incomplete / misleading about var() fallback usage mdn/content#44011
- 4: [css-variables] Computed value of custom property referring invalid custom property w3c/csswg-drafts#1837
- 5: https://drafts.csswg.org/css-variables-2/
- 6: https://drafts.csswg.org/css-variables-1/
- 7: https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/var
- 8: https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Cascading_variables/Using_custom_properties
- 9: [css-variables] Substitution of invalid variables into other variables w3c/csswg-drafts#4075
- 10: https://drafts.csswg.org/css-variables/
🌐 Web query:
self-referential custom property var(--x, fallback) computed value same property cycle behavior
💡 Result:
In CSS, custom properties that form a cyclic dependency (e.g., --a: var(--b); --b: var(--a);) are considered "invalid at computed-value time" (IACVT) [1][2][3]. When a custom property is IACVT, it does not resolve to a usable value; instead, it adopts the "guaranteed-invalid" value [4][5][3]. Regarding the behavior of var(--x, fallback) in the context of such cycles: 1. Cycle Detection: If a custom property references itself (directly or indirectly through other custom properties), it and all properties involved in that dependency cycle are marked as IACVT [2][6][3]. 2. Fallback Resolution: The var function's fallback mechanism is only triggered when the referenced custom property is invalid or missing (i.e., currently holds the guaranteed-invalid value) [4][5][7]. 3. Interaction: When a custom property within a cycle is referenced by another property, the referring property will attempt to substitute the value. Because the custom property in the cycle is IACVT (guaranteed-invalid), the var function will successfully fall back to the provided fallback value, if one exists [5][7][3]. If a property referencing a cycle has a fallback, it uses that fallback [5][3]. If it does not have a fallback, the property itself becomes IACVT and resolves to its initial or inherited value (as if it were set to the unset keyword), rather than propagating the cycle's invalidity in a way that creates an empty string or crash [1][2][3]. Note: While older implementations or specific edge cases occasionally sparked debate, the standardized behavior is that cycles trigger the guaranteed-invalid value, which then allows var fallbacks to be used if they were provided [5][1][8][3].
Citations:
- 1: [css-variables] Computed value of custom property referring invalid custom property w3c/csswg-drafts#1837
- 2: https://www.w3.org/TR/css-variables-1/
- 3: https://drafts.csswg.org/css-variables-1/
- 4: https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/var
- 5: https://www.w3.org/TR/css-variables/
- 6: https://kizu.dev/cyclic-toggles/
- 7: https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Cascading_variables/Using_custom_properties
- 8: [css-properties-values-api] Support fallbacks. web-platform-tests/wpt#14039
🌐 Web query:
"var(--font-serif, Georgia)" self-referential custom property same property fallback browser behavior
💡 Result:
In CSS, a self-referential custom property—such as using var(--x, value) to define --x itself—is valid syntax but creates a dependency that affects how the browser resolves the value [1][2]. The fallback mechanism in the var function only triggers when the referenced custom property is invalid or guaranteed-invalid (e.g., unset or set to the initial keyword) [1][3][4]. When you define a property using its own name as the variable within a var function, the browser behavior is as follows: 1. Self-Reference Logic: If you declare --font-serif: var(--font-serif, Georgia), the browser attempts to resolve --font-serif [1]. Because the property is currently being defined, it does not yet have a usable value in the scope of that declaration, making it effectively invalid [1][2]. 2. Fallback Trigger: Consequently, the browser treats the variable as having the guaranteed-invalid value and falls back to the provided value (in your example, Georgia) [1][3]. 3. Computed-Value Time: It is important to note that if this variable is used elsewhere, the browser resolves it based on the value inherited or defined. If a variable references itself in a way that creates a cyclic dependency (e.g., --a: var(--b); --b: var(--a)), the entire chain becomes invalid at computed-value time, causing the properties to revert to their inherited values or the initial value of the property [1]. In the specific case of --font-serif: var(--font-serif, Georgia), the fallback is successfully applied because the custom property is not yet defined in the current rule's context [1][2]. This is a common pattern used to provide default values for variables [5][6]. If the custom property is later defined in a child scope or a different selector, that new definition will take precedence over the fallback [5][7]. Top results: [1], [2], [3], [4]
Citations:
- 1: https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/var
- 2: https://www.w3.org/TR/2015/CR-css-variables-1-20151203/
- 3: https://master.dev/blog/custom-property-fallbacks/
- 4: https://stackoverflow.com/questions/66987917/css-var-my-var-fallback-with-initial-value-is-it-a-bug
- 5: https://ryanmulligan.dev/blog/css-custom-prop-fallbacks/
- 6: https://slides.oddbird.net/variables/aea2104/
- 7: https://tailwindcss.com/docs/theme
🌐 Web query:
CSS variables --x: var(--x, fallback) self-reference same property spec fallback invalid at computed-value time
💡 Result:
When a custom property is defined with a self-referencing var() function—such as --x: var(--x, fallback);—the browser evaluates the dependency graph during computed-value time [1][2]. Because the property depends on itself, this creates a cyclic dependency [3]. According to the CSS specification, when a cycle is detected in the dependency graph of custom properties, all properties involved in that cycle are considered invalid at computed-value time (IACVT) [3]. Because the property is IACVT, its value cannot be successfully substituted, and the var() function fails to resolve to the provided fallback [1][2]. Instead of falling back to the value provided in the var() function, the property resets to its initial value [1][4]. For non-registered custom properties, this initial value is the "guaranteed-invalid" value [1][5]. If the custom property is registered via @property with an initial-value descriptor, it will reset to that specified initial value instead [6][7][8]. In summary, you cannot use var(--x, fallback) to maintain a previous value or create a conditional fallback for the same property --x [9]. The self-reference triggers cycle detection, causing the declaration to be discarded and the property to reset to its initial state [4][3][9].
Citations:
- 1: https://www.w3.org/TR/css-variables/
- 2: https://www.w3.org/TR/css-variables-1/
- 3: https://pawelgrzybek.com/til-invalid-css-cyclic-dependencies-and-a-valid-empty-fallback-value-using-a-bare-comma/
- 4: [css-variables] Computed value of custom property referring invalid custom property w3c/csswg-drafts#1837
- 5: https://drafts.csswg.org/css-variables-1/
- 6: https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/var
- 7: https://www.w3.org/TR/css-properties-values-api-1/
- 8: https://moderncss.dev/how-custom-property-values-are-computed/
- 9: https://stackoverflow.com/questions/44562399/css-variable-with-fallback-value-of-itself
Avoid self-referential font tokens. --font-serif, --font-sans, and --font-mono reference themselves here, so if the Next font classes don’t win the cascade these tokens become invalid instead of falling back to Georgia/system-ui/ui-monospace. Use separate --next-font-* variables and map the semantic tokens to those instead.
🤖 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/globals.css` around lines 33 - 35, Update the font token declarations in
the global styles to avoid self-references: define separate --next-font-serif,
--next-font-sans, and --next-font-mono fallback variables, then map
--font-serif, --font-sans, and --font-mono to the corresponding --next-font-*
variables. Preserve the existing Georgia, system-ui, and ui-monospace fallbacks.
| day_button: 'h-8 w-8 flex items-center justify-center font-mono text-[12.5px] text-[var(--ink)] cursor-pointer hover:bg-[var(--tan-2)] hover:text-[var(--ink-on-tan)] transition-colors duration-100 w-full h-full', | ||
| selected: '[&>button]:bg-[var(--ink)] [&>button]:text-[var(--paper)] [&>button]:hover:bg-[var(--ink)]', | ||
| today: '[&>button]:border [&>button]:border-[var(--ink)]', | ||
| outside: 'opacity-30', | ||
| disabled: 'opacity-20 pointer-events-none', | ||
| range_middle: '[&>button]:bg-[var(--tan-2)] [&>button]:rounded-none', | ||
| range_middle: '[&>button]:bg-[var(--tan-2)] [&>button]:text-[var(--ink-on-tan)] [&>button]:rounded-none', |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== calendar.tsx ==\n'
sed -n '120,190p' components/ui/calendar.tsx
printf '\n== color variables ==\n'
rg -n --no-heading --glob '!node_modules' --glob '!dist' --glob '!build' --glob '!coverage' --glob '*.css' --glob '*.tsx' --glob '*.ts' --glob '*.jsx' --glob '*.js' --glob '*.md' --glob '*.mdx' '--(--ink|--paper|--tan-2|--ink-on-tan)' .
printf '\n== file list hints ==\n'
git ls-files | rg '(^|/)(globals\.css|theme|variables|calendar\.tsx)$'Repository: Retsomm/SelfMap
Length of output: 2753
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '1,220p' components/ui/calendar.tsxRepository: Retsomm/SelfMap
Length of output: 6757
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== variable definitions ==\n'
rg -n --no-heading --glob '!node_modules' --glob '!dist' --glob '!build' --glob '!coverage' \
--glob '*.css' --glob '*.scss' --glob '*.ts' --glob '*.tsx' --glob '*.js' --glob '*.jsx' \
-- '--ink:' '--paper:' '--tan-2:' '--ink-on-tan:' '--ink-soft:' .
printf '\n== tailwind / config references ==\n'
rg -n --no-heading --glob '!node_modules' --glob '!dist' --glob '!build' --glob '!coverage' \
'ink-on-tan|paper-deep|tan-2|ink-soft' tailwind.config.* **/*.css **/*.ts **/*.tsx **/*.js **/*.jsxRepository: Retsomm/SelfMap
Length of output: 479
🏁 Script executed:
#!/bin/bash
set -euo pipefail
for pat in '--ink' '--paper' '--tan-2' '--ink-on-tan' '--ink-soft' '--paper-deep'; do
printf '\n== %s ==\n' "$pat"
rg -n --no-heading --glob '!node_modules' --glob '!dist' --glob '!build' --glob '!coverage' -- "$pat" .
doneRepository: Retsomm/SelfMap
Length of output: 50372
🌐 Web query:
react-day-picker classNames selected day_button wrapper button selector specificity
💡 Result:
In React DayPicker (v9+), the styling architecture has shifted to improve accessibility, which directly affects how you must handle selector specificity when targeting day buttons [1][2]. Key Structural Changes The "selected" state is now applied to the grid cell element (typically a td or similar container) rather than the inner button element [2]. The button itself is identified by the day_button class [1][3]. Styling the Selected Day Button Because the.selected class is on the container and not the button, a common pitfall is attempting to style the button directly using a selector like.rdp-selected. To style the button when a day is selected, you must target the child button element within the selected container [2]. Recommended CSS Approaches: 1. Target the child button: Use a descendant combinator in your CSS [2]..rdp-selected >.rdp-day_button { /* Your custom styles / } 2. CSS Specificity Hack: If default library styles are overriding your custom rules, you can increase the specificity of your selector by repeating the class name [4]..rdp-root.rdp-root.rdp-selected >.rdp-day_button { / Higher specificity styles */ } 3. Tailwind CSS: If you are using Tailwind, you can use the arbitrary variant syntax to target the child button directly from the container's class configuration [5][2]. <DayPicker classNames={{ selected: "[&>.rdp-day_button]:bg-blue-500", }} /> Best Practices - Prefer the classNames prop: When using CSS modules or framework-specific utilities (like Tailwind), define your styles via the classNames prop on the DayPicker component rather than relying solely on global CSS overrides [6][7]. - CSS Variables: For simple color changes (accent colors, borders, etc.), check if the library's CSS variables (scoped to.rdp-root) can achieve your goal before writing complex custom selectors, as this is the intended way to theme the component [7][4]. - Import Order: Ensure your custom stylesheet is imported after the library's default CSS to prevent the library's default styles from inadvertently taking precedence due to cascade order [4].
Citations:
- 1: https://daypicker.dev/upgrading-v8-to-v10
- 2: Selected classname is set on <td> not <button> gpbl/react-day-picker#2660
- 3: https://github.com/gpbl/react-day-picker/blob/main/packages/react-day-picker/src/UI.ts
- 4: Can't change accent color gpbl/react-day-picker#2810
- 5:
range_startandselectclasses do not work together when styling with tailwind css gpbl/react-day-picker#2309 - 6: https://daypicker.dev/docs/styling
- 7: https://daypicker.dev/v9/docs/styling
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '1,70p' app/globals.cssRepository: Retsomm/SelfMap
Length of output: 1853
Keep selected-day text light on hover. components/ui/calendar.tsx:154-159
day_button still applies hover:text-[var(--ink-on-tan)], which overrides the selected-day text color on hover. In the light theme that makes the selected date dark on a dark --ink background.
🤖 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 `@components/ui/calendar.tsx` around lines 154 - 159, Update the calendar class
configuration around day_button and selected so selected days retain the light
paper text color on hover; ensure the selected-state hover text utility takes
precedence over day_button’s hover:text-[var(--ink-on-tan)] without changing
unselected-day hover styling.
| {/* 行星閘門對照 */} | ||
| {planetsA.length > 0 && ( | ||
| <View style={c.section}> | ||
| <Text style={c.sectionLabel}>行星閘門對照</Text> | ||
| <View style={c.card}> | ||
| <View style={c.planetGroupRow}> | ||
| <View style={c.planetPlanetCol} /> | ||
| <Text style={[c.planetGroupLabel, { color: Colors.accent }]}>{nameA}</Text> | ||
| <Text style={[c.planetGroupLabel, { color: Colors.text }]}>{nameB}</Text> | ||
| </View> | ||
| <View style={[c.planetRow, c.planetHeaderRow]}> | ||
| <Text style={[c.planetPlanetCol, c.planetHeaderText]}>行星</Text> | ||
| <Text style={[c.planetGateColSm, c.planetHeaderText, { color: Colors.text }]}>意識</Text> | ||
| <Text style={[c.planetGateColSm, c.planetHeaderText, { color: Colors.designRed }]}>潛意識</Text> | ||
| <Text style={[c.planetGateColSm, c.planetHeaderText, { color: Colors.text }]}>意識</Text> | ||
| <Text style={[c.planetGateColSm, c.planetHeaderText, { color: Colors.designRed }]}>潛意識</Text> | ||
| </View> | ||
| {planetsA.map((p, i) => { | ||
| const pb = planetsB[i] | ||
| return ( | ||
| <View key={p.name} style={[c.planetRow, i % 2 === 1 && c.planetRowAlt]}> | ||
| <Text style={c.planetPlanetCol}>{p.name}</Text> | ||
| <Text style={[c.planetGateColSm, { color: Colors.text }]}>{p.blackGate}.{p.blackLine}</Text> | ||
| <Text style={[c.planetGateColSm, { color: Colors.designRed }]}>{p.redGate}.{p.redLine}</Text> | ||
| <Text style={[c.planetGateColSm, { color: Colors.text }]}>{pb?.blackGate}.{pb?.blackLine}</Text> | ||
| <Text style={[c.planetGateColSm, { color: Colors.designRed }]}>{pb?.redGate}.{pb?.redLine}</Text> | ||
| </View> | ||
| ) | ||
| })} | ||
| </View> | ||
| </View> | ||
| )} | ||
|
|
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Planet-table group headers misaligned with data columns.
The "行星閘門對照" section (Lines 111-143) header row assigns planetGroupLabel a flex: 1 for each of nameA/nameB (Lines 118-119), but the data rows below (and the 意識/潛意識 sub-header row) use 4 planetGateColSm columns of flex: 1 each — i.e. 2 columns (flex 2 total) per person. Since the group label only occupies flex 1 while its two underlying columns occupy flex 2, the name headers won't be centered over the correct data columns.
For comparison, the PDF export version of this same table (mobile/lib/chartPdf.ts's buildCompositeHtml) correctly uses <th colspan="2"> for the equivalent group header, confirming the intended layout is for each name to span both of its columns.
🐛 Proposed fix
planetGroupRow: { flexDirection: 'row', padding: 14, paddingBottom: 2 },
planetPlanetCol: { flex: 1.2, fontSize: 12, color: Colors.sub },
- planetGroupLabel: { flex: 1, fontSize: 11, fontWeight: '700', textAlign: 'center' },
+ planetGroupLabel: { flex: 2, fontSize: 11, fontWeight: '700', textAlign: 'center' },🤖 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 `@mobile/components/chart/CompositeInfo.tsx` around lines 111 - 143, Update the
行星閘門對照 group-header row in the CompositeInfo component so each planetGroupLabel
for nameA and nameB spans or occupies the combined width of its two underlying
planetGateColSm columns, matching the four-column data and sub-header rows.
Preserve the leading empty planet column and keep the two person groups centered
over their respective 意識/潛意識 pairs.
| // 明色盤對應網頁版 :root,暗色盤對應網頁版 :root[data-theme="dark"], | ||
| // 兩邊色票需保持同步(見 app/globals.css) | ||
|
|
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Compare hex values between mobile tokens and web globals.css for a few key tokens
rg -n "transitWarmText|successText|successBg" -A1 mobile/constants/tokens.ts
rg -n "\-\-crimson|\-\-olive|\-\-mustard|\-\-tan" app/globals.cssRepository: Retsomm/SelfMap
Length of output: 1451
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- mobile/constants/tokens.ts ---'
sed -n '1,220p' mobile/constants/tokens.ts
echo
echo '--- app/globals.css (theme variable sections) ---'
sed -n '1,120p' app/globals.cssRepository: Retsomm/SelfMap
Length of output: 7146
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
import re
tokens = Path('mobile/constants/tokens.ts').read_text()
# Extract light and dark token blocks if present in simple object literals
for name in ['lightColors', 'darkColors']:
m = re.search(rf'{name}\s*=\s*\{{(.*?)\n\}}', tokens, re.S)
if m:
print(f'--- {name} ---')
body = m.group(1)
for line in body.splitlines():
if ':' in line:
print(line.rstrip())
print()
PYRepository: Retsomm/SelfMap
Length of output: 153
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- token name usages ---'
rg -n "transitWarmText|successText|successBg" mobile app -g '!**/node_modules/**' -g '!**/.next/**'
echo
echo '--- web palette variables ---'
rg -n --context 1 --fixed-strings -- '--mustard' app/globals.css
rg -n --context 1 --fixed-strings -- '--olive' app/globals.css
rg -n --context 1 --fixed-strings -- '--tan:' app/globals.css
rg -n --context 1 --fixed-strings -- '--crimson' app/globals.cssRepository: Retsomm/SelfMap
Length of output: 3211
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n "transitWarmText|successText|successBg" .Repository: Retsomm/SelfMap
Length of output: 1322
🏁 Script executed:
#!/bin/bash
set -euo pipefail
nl -ba mobile/constants/tokens.ts | sed -n '1,220p'Repository: Retsomm/SelfMap
Length of output: 193
Keep the new status colors aligned with the shared web palette
transitWarmText, successText, and successBg are new semantic tokens, but the web theme only defines the shared palette in app/globals.css. If they’re meant to mirror the desktop colors, use the same hex values; otherwise mark them as mobile-only to avoid palette drift.
🤖 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 `@mobile/constants/tokens.ts` around lines 2 - 4, Update the new semantic
tokens transitWarmText, successText, and successBg in the mobile color palette
to use the matching hex values from app/globals.css, or explicitly document them
as mobile-only if no web equivalents are intended; keep shared palette tokens
synchronized across light and dark themes.
| useEffect(() => { | ||
| AsyncStorage.getItem(STORAGE_KEY).then(stored => { | ||
| if (stored === 'light' || stored === 'dark') setMode(stored) | ||
| }).catch(() => {}) | ||
| }, []) | ||
|
|
||
| const toggleTheme = useCallback(() => { | ||
| setMode(prev => { | ||
| const next: ThemeMode = prev === 'dark' ? 'light' : 'dark' | ||
| AsyncStorage.setItem(STORAGE_KEY, next).catch(() => {}) | ||
| return next | ||
| }) | ||
| }, []) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="mobile/contexts/ThemeContext.tsx"
echo "== file outline =="
ast-grep outline "$FILE" --view expanded || true
echo
echo "== relevant lines =="
nl -ba "$FILE" | sed -n '1,220p'Repository: Retsomm/SelfMap
Length of output: 582
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="mobile/contexts/ThemeContext.tsx"
echo "== relevant source =="
awk 'NR>=1 && NR<=220 { printf "%4d %s\n", NR, $0 }' "$FILE"Repository: Retsomm/SelfMap
Length of output: 2254
Prevent hydration from overwriting a newer theme choice.
AsyncStorage.getItem(STORAGE_KEY) can resolve after toggleTheme() runs and call setMode(stored), rolling back the user's latest selection. Gate the initial restore, or ignore it once a local toggle has happened.
🤖 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 `@mobile/contexts/ThemeContext.tsx` around lines 23 - 35, Update the
initialization effect and toggleTheme callback in ThemeContext so an
asynchronously resolved stored theme cannot overwrite a mode changed locally by
toggleTheme. Track whether a local toggle has occurred, and apply the restored
value only if no toggle happened before the storage read completed; preserve the
existing light/dark validation and persistence behavior.
| function pdfPalette(mode: PdfThemeMode) { | ||
| const c = mode === 'dark' ? darkColors : lightColors | ||
| return { | ||
| bg: c.bg, | ||
| ink: c.text, | ||
| crimson: c.accent, | ||
| sub: c.sub, | ||
| cardBg: c.surface, | ||
| border: c.border, | ||
| dimBg: mode === 'dark' ? darkColors.altRowBg : lightColors.gateBg, | ||
| altRow: c.altRowBg, | ||
| paperDeep: c.gateBg, // 對應網頁版 --paper-deep,用於頁尾商標色帶 | ||
| accentBg: c.accentD, // 對應畫面 Tag active 的底色 | ||
| dimText: c.planetRedText, // 對應畫面 Row dim 的文字色 | ||
| } | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
Is Tailwind CSS v4 dark mode contrast typically verified against WCAG AA for custom token palettes?
💡 Result:
In Tailwind CSS v4, contrast verification against WCAG AA standards for custom token palettes is not an automatic feature of the framework, but it is a critical, industry-standard best practice implemented manually by developers [1][2][3]. While Tailwind v4 provides the tools to build accessible systems—most notably through the @theme directive and CSS custom properties—it does not inherently validate or enforce WCAG compliance [4][3][5][6]. Verification is typically achieved through the following approaches: 1. Manual or Automated Auditing: Teams frequently use automated testing tools (such as Playwright with axe-core) to audit contrast ratios across themes (light/dark) in CI pipelines [7][8]. Dedicated contrast checkers for Tailwind colors are also commonly used to verify pairs before they are finalized in the @theme block [9]. 2. Perceptual Uniformity: Many modern v4 setups leverage the OKLCH color space for custom tokens because its lightness (L) channel serves as a reliable proxy for perceived brightness [5][6]. This makes it significantly easier to mathematically predict and maintain accessible contrast ratios when switching between light and dark backgrounds [5][8]. 3. Strategic Token Architecture: To ensure compliance, developers define semantic color tokens (e.g., --color-surface, --color-on-surface) in the @theme block [3][8]. These tokens are then overridden in a dark-mode variant (e.g.,.dark or [data-theme=dark]) [3][10][11]. By using CSS variables instead of hard-coded values, the system ensures that utilities like text-surface maintain valid contrast ratios in every state [7]. 4. Avoiding Common Pitfalls: A well-documented issue in v4 involves the use of @theme inline versus @theme. Using @theme inline bakes hard-coded values into CSS classes, preventing them from being overridden by theme-specific CSS cascades [7]. Experienced teams use @theme for tokens to ensure the cascade can successfully swap values while maintaining accessibility [7]. Ultimately, WCAG AA compliance is a manual responsibility: developers must audit every foreground-on-background color combination to ensure they meet the 4.5:1 minimum for normal text (or 3:1 for large text) across all supported modes [4][3][8].
Citations:
- 1: https://innatus.digital/digital-insights/our-tailwind-v4-design-system-and-how-we-handle-brand-tokens
- 2: https://z.tools/blog/accessible-tailwind-palette
- 3: https://webminty.com/posts/tailwind/dark-mode-in-tailwind-beyond-just-swapping-colors
- 4: https://venturecrane.com/articles/building-dark-theme-design-system/
- 5: https://richdynamix.com/articles/tailwind-v4-color-mix-oklch-design-tokens
- 6: https://combray.prose.sh/2025-11-30-tailwind-best-practices
- 7: https://dev.arabicstore1.workers.dev/forrestmiller/tailwind-v4-dark-mode-the-theme-vs-theme-inline-gotcha-that-broke-my-contrast-tests-3p3o
- 8: https://llmbestpractices.com/frontend/tailwind-dark-mode
- 9: https://tailwindcolor.tools/tailwind-contrast-checker
- 10: https://tailwindcss.com/docs/dark-mode
- 11: https://abrarqasim.com/blog/tailwind-v4-dark-mode-from-config-to-one-css-line/
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== chartPdf.ts outline ==\n'
ast-grep outline mobile/lib/chartPdf.ts --view expanded || true
printf '\n== tokens file candidates ==\n'
fd -a 'tokens.ts' mobile || true
printf '\n== search for dimBg / section-title / tag usage ==\n'
rg -n "dimBg|section-title|tag" mobile/lib/chartPdf.ts mobile -g '*.ts' -g '*.tsx' || true
printf '\n== inspect chartPdf.ts around palette and CSS usages ==\n'
sed -n '1,120p' mobile/lib/chartPdf.ts
printf '\n---- later sections ----\n'
sed -n '400,460p' mobile/lib/chartPdf.ts
printf '\n---- later sections 2 ----\n'
sed -n '940,990p' mobile/lib/chartPdf.ts
printf '\n---- later sections 3 ----\n'
sed -n '1136,1160p' mobile/lib/chartPdf.ts
printf '\n== inspect tokens.ts if present ==\n'
TOKENS="$(fd -a 'tokens.ts' mobile | head -n 1 || true)"
if [ -n "${TOKENS:-}" ]; then
wc -l "$TOKENS"
sed -n '1,240p' "$TOKENS"
fiRepository: Retsomm/SelfMap
Length of output: 29972
Use c.gateBg for dimBg in mobile/lib/chartPdf.ts
dimBg backs .section-title and .tag, but darkColors.altRowBg is almost indistinguishable from darkColors.surface, so the section headers and chips lose contrast in dark-mode PDFs. c.gateBg keeps the same visual role in both themes.
🤖 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 `@mobile/lib/chartPdf.ts` around lines 34 - 49, Update the dimBg assignment in
pdfPalette to use c.gateBg for both dark and light themes, replacing the
mode-specific darkColors.altRowBg fallback while preserving the existing palette
structure.
Summary by CodeRabbit
New Features
Bug Fixes