Skip to content

feat: 新增全站深色模式主題,修正人類圖 PDF 報告內容一致性與檔名格式#80

Open
Retsomm wants to merge 1 commit into
mainfrom
dev
Open

feat: 新增全站深色模式主題,修正人類圖 PDF 報告內容一致性與檔名格式#80
Retsomm wants to merge 1 commit into
mainfrom
dev

Conversation

@Retsomm

@Retsomm Retsomm commented Jul 22, 2026

Copy link
Copy Markdown
Owner
  • web/mobile 加入明暗主題切換(ThemeProvider/ThemeContext/ThemeToggle),globals.css 與各元件配色跟著主題走
  • mobile 人類圖 PDF 報告(個人/合圖/流日)內容修正,跟畫面(儲存前預覽/已儲存詳情頁)完全對齊:
    • 補齊類型策略/簽名/非自我主題等缺漏欄位、九大中心與定義通道改用中文標籤並標示啟用狀態
    • 修正合圖行星表格配色語意、連結動力顏色改跟著明暗主題走、移除誤導性的原始整合代碼
    • 修正流日 Body Graph 黑紅語意錯誤(改用「個人 vs 今日流日」而非 Personality/Design)、重建行星閘門對照表與流日影響分析卡片
    • 三份報告共用的版面小工具(row/tags/section 等)合併成模組層級函式,避免各自維護造成內容drift
  • PDF 下載檔名改為「圖表種類-圖表名稱-下載時間.pdf」,透過 expo-file-system 複製成可讀檔名再分享

Summary by CodeRabbit

  • New Features

    • Added light and dark theme switching across web and mobile, with preferences saved for future visits.
    • Added a theme toggle to navigation and mobile tab controls.
    • Updated chart, transit, composite, and learning screens to adapt their colors automatically.
    • Added clearer transit impact cards, gate chips, and planet comparisons.
    • Enhanced exported PDFs with theme-matched styling and improved chart details.
  • Bug Fixes

    • Improved contrast for calendar, selection, error, hover, and active navigation states.
    • Preserved selected timezones when saving birth profiles.

- 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>
@vercel

vercel Bot commented Jul 22, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
self-map Ready Ready Preview, Comment Jul 22, 2026 1:44pm

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

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

Changes

Web theme and Human Design styling

Layer / File(s) Summary
Theme initialization and controls
app/layout.tsx, components/ThemeProvider.tsx, components/ThemeToggle.tsx, components/Navbar.tsx, lib/theme.ts
The web theme is initialized from a cookie, applied to <html data-theme>, persisted when toggled, and exposed through desktop and mobile navigation controls.
Theme token migration
app/globals.css, components/humanDesign/*, components/SelfMap.tsx, lib/clerkAppearance.ts, components/ui/*, app/aura-flow/page.tsx
Hardcoded colors, borders, shadows, overlays, legends, and state styling are replaced with shared CSS theme variables.
Chart and drawer presentation
components/humanDesign/DetailDrawer.tsx, components/humanDesign/TransitView.tsx, components/humanDesign/CompositeView.tsx
Human Design detail, transit, and composite views use semantic theme colors while preserving their existing data flows.
PDF chart capture
lib/downloadChart.ts
Chart image dimensions, excluded-node measurement, footer sizing, and PDF footer colors are derived from the current document theme.

Mobile theming and chart presentation

Layer / File(s) Summary
Theme palettes and provider
mobile/constants/tokens.ts, mobile/contexts/ThemeContext.tsx, mobile/app/_layout.tsx, mobile/app/(tabs)/_layout.tsx
Mobile light and dark palettes are introduced, persisted through AsyncStorage, provided through hooks, and exposed through a non-navigating tab toggle.
Runtime-themed screens and components
mobile/app/**, mobile/components/{AppliedProfileCard,BirthDataForm,DateTimePicker,DetailBottomSheet,ErrorBoundary,InputModal,NavBackHeader,NotificationsView,StateViews,SubTabBar,TimezonePickerModal,WheelPicker}.tsx
Static React Native styles are converted into memoized factories using the active theme colors.
Learning and chart primitives
mobile/components/learn/*, mobile/components/chart/ChartPrimitives.tsx, mobile/components/chart/PersonalChartDetails.tsx
Learning styles, accordion props, chart primitives, and personal chart details now receive runtime theme palettes.
Composite and transit UI
mobile/components/composite/*, mobile/components/chart/CompositeInfo.tsx, mobile/components/CompositeView.tsx, mobile/components/transit/*, mobile/components/TransitView.tsx, mobile/components/chart/TransitAnalysis.tsx
Shared composite descriptors, planet comparison content, gate chips, impact cards, icons, and themed transit analysis replace local presentation structures.
Themed PDF exports
mobile/lib/chartPdf.ts, mobile/app/chart/*
PDF builders accept theme mode and optional chart names, render palette-based personal/composite/transit layouts, and receive mode from chart download actions.

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
Loading

Possibly related PRs

  • Retsomm/SelfMap#3 — Modifies the shared navbar component used by this PR’s theme toggle and active-route handling.
  • Retsomm/SelfMap#25 — Introduces composite components whose accent styling is updated here.
  • Retsomm/SelfMap#46 — Provides related mobile design-system work extended here with light/dark palettes and runtime theme styles.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.91% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Title matches the main changes: app-wide theme support plus human design PDF content and filename updates.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

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.

❤️ Share

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

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 6

🧹 Nitpick comments (3)
mobile/components/NotificationsView.tsx (1)

19-23: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Share themed styles and configuration across notification rows.

NotificationCard rebuilds the same StyleSheet and type configuration once per FlatList item. Compute them once in NotificationsView and 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.announcement
 export 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 win

Document the new theme-switching behavior.

  • components/ThemeProvider.tsx#L17-L24: add a complete comment covering the state transition, DOM data-theme update, and cookie persistence contract.
  • components/ThemeToggle.tsx#L5-L15: add a component-level comment covering the theme-state-to-icon, action-label, and aria-pressed mapping.

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 win

Extract a shared ActionButton into ChartPrimitives.tsx. Both chart screens independently define an ActionButton component plus identical actionBtn* style keys inside their own createStyles(Colors), right alongside the same PR's centralization of SectionCard/Row/Tag into ChartPrimitives.tsx.

  • mobile/app/chart/[id].tsx#L55-L84: move this ActionButton (and its actionBtn* style keys) into ChartPrimitives.tsx, exporting a shared component parameterized by the existing props.
  • mobile/app/chart/preview.tsx#L32-L65: replace this variant (which adds notLoggedInLabel/isLoggedIn) with the shared component, extending its props to support the not-logged-in label case, and drop the duplicated actionBtn* keys from its local createStyles.
🤖 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

📥 Commits

Reviewing files that changed from the base of the PR and between 653633b and 1680659.

⛔ Files ignored due to path filters (1)
  • mobile/assets/store/icon-512.png is excluded by !**/*.png
📒 Files selected for processing (70)
  • app/account/AccountClient.tsx
  • app/aura-flow/page.tsx
  • app/globals.css
  • app/layout.tsx
  • components/Navbar.tsx
  • components/SelfMap.tsx
  • components/ThemeProvider.tsx
  • components/ThemeToggle.tsx
  • components/humanDesign/BirthProfileManager.tsx
  • components/humanDesign/BodyGraph.tsx
  • components/humanDesign/ChartView.tsx
  • components/humanDesign/CompositeTab.tsx
  • components/humanDesign/CompositeView.tsx
  • components/humanDesign/DetailDrawer.tsx
  • components/humanDesign/LocationPicker.tsx
  • components/humanDesign/TimezonePickerModal.tsx
  • components/humanDesign/TransitView.tsx
  • components/ui/calendar.tsx
  • components/ui/select.tsx
  • lib/clerkAppearance.ts
  • lib/downloadChart.ts
  • lib/theme.ts
  • mobile/app/(auth)/sign-in.tsx
  • mobile/app/(tabs)/_layout.tsx
  • mobile/app/(tabs)/create.tsx
  • mobile/app/(tabs)/index.tsx
  • mobile/app/(tabs)/learn.tsx
  • mobile/app/(tabs)/profile.tsx
  • mobile/app/(tabs)/theme-toggle.tsx
  • mobile/app/_layout.tsx
  • mobile/app/chart/[id].tsx
  • mobile/app/chart/preview.tsx
  • mobile/app/learn/[topic].tsx
  • mobile/components/AppliedProfileCard.tsx
  • mobile/components/BirthDataForm.tsx
  • mobile/components/BirthProfilePickerModal.tsx
  • mobile/components/BirthProfileSheet.tsx
  • mobile/components/ChartListView.tsx
  • mobile/components/CitySearchField.tsx
  • mobile/components/CompositeView.tsx
  • mobile/components/DateTimePicker.tsx
  • mobile/components/DetailBottomSheet.tsx
  • mobile/components/ErrorBoundary.tsx
  • mobile/components/InputModal.tsx
  • mobile/components/NavBackHeader.tsx
  • mobile/components/NotificationsView.tsx
  • mobile/components/ScreenHeader.tsx
  • mobile/components/StateViews.tsx
  • mobile/components/SubTabBar.tsx
  • mobile/components/TimezonePickerModal.tsx
  • mobile/components/TransitView.tsx
  • mobile/components/WheelPicker.tsx
  • mobile/components/chart/ChartPrimitives.tsx
  • mobile/components/chart/CompositeInfo.tsx
  • mobile/components/chart/PersonalChartDetails.tsx
  • mobile/components/chart/TransitAnalysis.tsx
  • mobile/components/composite/compositeText.ts
  • mobile/components/learn/CenterList.tsx
  • mobile/components/learn/ChannelList.tsx
  • mobile/components/learn/CompositeLearn.tsx
  • mobile/components/learn/GateList.tsx
  • mobile/components/learn/SummaryList.tsx
  • mobile/components/learn/TransitLearn.tsx
  • mobile/components/learn/learnStyles.ts
  • mobile/components/transit/GateChip.tsx
  • mobile/components/transit/ImpactCard.tsx
  • mobile/components/transit/icons.tsx
  • mobile/constants/tokens.ts
  • mobile/contexts/ThemeContext.tsx
  • mobile/lib/chartPdf.ts
💤 Files with no reviewable changes (1)
  • mobile/components/ScreenHeader.tsx

Comment thread app/globals.css
Comment on lines 33 to 35
--font-serif: var(--font-serif, "Georgia", serif);
--font-sans: var(--font-sans, system-ui, sans-serif);
--font-mono: var(--font-mono, ui-monospace, monospace);

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 | 🟡 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.css

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


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


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


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


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.

Comment on lines +154 to +159
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',

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 | 🟡 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.tsx

Repository: 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 **/*.jsx

Repository: 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" .
done

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


🏁 Script executed:

#!/bin/bash
set -euo pipefail
sed -n '1,70p' app/globals.css

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

Comment on lines +111 to +143
{/* 行星閘門對照 */}
{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>
)}

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

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.

Comment on lines +2 to 4
// 明色盤對應網頁版 :root,暗色盤對應網頁版 :root[data-theme="dark"],
// 兩邊色票需保持同步(見 app/globals.css)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ 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.css

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

Repository: 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()
PY

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

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

Comment on lines +23 to +35
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
})
}, [])

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

Comment thread mobile/lib/chartPdf.ts
Comment on lines +34 to +49
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 的文字色
}
}

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


🏁 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"
fi

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

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.

1 participant