mirror of
https://github.com/multica-ai/multica.git
synced 2026-08-12 10:59:06 +02:00
Two related fixes that share root cause: shadcn's neutral palette
collapses `secondary` / `muted` / `accent` to the SAME L 96.1% value
intentionally — it's a single tonal slot whose semantic name varies by
use case, not three different colors. Stacking a bg-muted child on a
bg-secondary parent (which is what we were doing for code/table headers
inside the comment bubble) made the inner element visually disappear.
Introduce a proper 5-tier elevation scale calibrated to Refactoring UI
and Material 3 guidance:
L 100 page bg / card / popover (page floor)
L 98 surface-1 NEW (subtle elevated — comment
bubbles, iOS settings-cell
feel: visible boundary
via radius + border, fill
is almost-page)
L 96.1 secondary / muted / accent (shadcn default, untouched —
button hover, chips, skeleton)
L 90 surface-2 NEW (nested inside surface-1 —
table headers + code blocks
inside comment bubbles, 8% L
step over surface-1)
L 84 border (was 89.8% → 84%) (visible across every tier,
6-16% darker than adjacent
surface, within Refactoring
UI's 5-10% guideline)
Dark mirror flips the lightness direction (higher elevation = lighter):
page 3.9 → surface-1 8 → secondary 14.9 → surface-2 19 → border 25.
Applied across three files:
- global.css + tailwind.config.js + lib/theme.ts mirror the new tokens
(CSS variables, Tailwind class map, TypeScript export — they must
stay in sync per CLAUDE.md §5).
- components/issue/comment-card.tsx switches the bubble bg from
`bg-secondary` (too prominent, same color as inner muted elements)
to `bg-surface-1` (subtle, 8% lighter than inner surface-2).
- lib/markdown/markdown-style.ts:
- table.headerBackgroundColor + codeBlock.backgroundColor:
`t.muted` → `t.surface2`, so they're framed against the bubble.
- inline `code:`: REVERT 2026-05-19's `color: t.brand` workaround
for upstream enriched-markdown #255. The brand-tint avoided the
chip's top-heavy padding artifact but broke Refactoring UI's #1
rule (color carries semantic meaning — brand IS the link color,
users reported tapping inline code thinking it was a link).
Re-enable bg-chip + foreground text, matching GitHub mobile /
Slack / Notion / Apple Notes. The padding artifact is the lesser
evil; in surface-2 (L 90%) on surface-1 (L 98%) the chip is
subtle enough that the few pixels of asymmetry are unobtrusive.
The shadcn `secondary` / `muted` / `accent` tokens stay at L 96.1%
unchanged — other call sites (button hover, skeleton, avatar fallback,
chips) all work fine on their own and were never the problem.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
121 lines
3.8 KiB
TypeScript
121 lines
3.8 KiB
TypeScript
/**
|
|
* TypeScript mirror of the CSS variables defined in apps/mobile/global.css.
|
|
*
|
|
* - `THEME` is the raw token object for inline styles, animations, and
|
|
* anywhere a Tailwind class can't reach.
|
|
* - `NAV_THEME` is the React Navigation theme — passed into <ThemeProvider />
|
|
* in app/_layout.tsx so headers, modals, and the back button match.
|
|
*
|
|
* If you change a variable in global.css, update the matching key here.
|
|
* See apps/mobile/docs/rnr-migration.md §5 for the sync rule.
|
|
*/
|
|
import { DarkTheme, DefaultTheme, type Theme } from "@react-navigation/native";
|
|
|
|
export const THEME = {
|
|
light: {
|
|
background: "hsl(0 0% 100%)",
|
|
foreground: "hsl(0 0% 3.9%)",
|
|
card: "hsl(0 0% 100%)",
|
|
cardForeground: "hsl(0 0% 3.9%)",
|
|
popover: "hsl(0 0% 100%)",
|
|
popoverForeground: "hsl(0 0% 3.9%)",
|
|
primary: "hsl(0 0% 9%)",
|
|
primaryForeground: "hsl(0 0% 98%)",
|
|
secondary: "hsl(0 0% 96.1%)",
|
|
secondaryForeground: "hsl(0 0% 9%)",
|
|
muted: "hsl(0 0% 96.1%)",
|
|
mutedForeground: "hsl(0 0% 45.1%)",
|
|
accent: "hsl(0 0% 96.1%)",
|
|
accentForeground: "hsl(0 0% 9%)",
|
|
destructive: "hsl(0 84.2% 60.2%)",
|
|
destructiveForeground: "hsl(0 0% 98%)",
|
|
border: "hsl(0 0% 84%)",
|
|
input: "hsl(0 0% 84%)",
|
|
ring: "hsl(0 0% 63%)",
|
|
radius: "0.625rem",
|
|
chart1: "hsl(12 76% 61%)",
|
|
chart2: "hsl(173 58% 39%)",
|
|
chart3: "hsl(197 37% 24%)",
|
|
chart4: "hsl(43 74% 66%)",
|
|
chart5: "hsl(27 87% 67%)",
|
|
|
|
// Multica custom
|
|
brand: "hsl(225 71% 58%)",
|
|
brandForeground: "hsl(0 0% 98%)",
|
|
success: "hsl(142 71% 45%)",
|
|
warning: "hsl(48 89% 47%)",
|
|
info: "hsl(217 91% 60%)",
|
|
priority: "hsl(25 95% 53%)",
|
|
codeSurface: "hsl(240 4% 92%)",
|
|
// Surface elevation tiers — see global.css for the full scale.
|
|
surface1: "hsl(0 0% 98%)",
|
|
surface2: "hsl(0 0% 90%)",
|
|
},
|
|
dark: {
|
|
background: "hsl(0 0% 3.9%)",
|
|
foreground: "hsl(0 0% 98%)",
|
|
card: "hsl(0 0% 3.9%)",
|
|
cardForeground: "hsl(0 0% 98%)",
|
|
popover: "hsl(0 0% 3.9%)",
|
|
popoverForeground: "hsl(0 0% 98%)",
|
|
primary: "hsl(0 0% 98%)",
|
|
primaryForeground: "hsl(0 0% 9%)",
|
|
secondary: "hsl(0 0% 14.9%)",
|
|
secondaryForeground: "hsl(0 0% 98%)",
|
|
muted: "hsl(0 0% 14.9%)",
|
|
mutedForeground: "hsl(0 0% 63.9%)",
|
|
accent: "hsl(0 0% 14.9%)",
|
|
accentForeground: "hsl(0 0% 98%)",
|
|
destructive: "hsl(0 70.9% 59.4%)",
|
|
destructiveForeground: "hsl(0 0% 98%)",
|
|
border: "hsl(0 0% 25%)",
|
|
input: "hsl(0 0% 25%)",
|
|
ring: "hsl(300 0% 45%)",
|
|
radius: "0.625rem",
|
|
chart1: "hsl(220 70% 50%)",
|
|
chart2: "hsl(160 60% 45%)",
|
|
chart3: "hsl(30 80% 55%)",
|
|
chart4: "hsl(280 65% 60%)",
|
|
chart5: "hsl(340 75% 55%)",
|
|
|
|
// Multica custom — dark mirrors light until demand
|
|
brand: "hsl(225 71% 58%)",
|
|
brandForeground: "hsl(0 0% 98%)",
|
|
success: "hsl(142 71% 45%)",
|
|
warning: "hsl(48 89% 47%)",
|
|
info: "hsl(217 91% 60%)",
|
|
priority: "hsl(25 95% 53%)",
|
|
// code-surface is the ONE exception that needs a real dark value —
|
|
// see global.css for rationale. Keep this in sync with .dark:root.
|
|
codeSurface: "hsl(240 4% 18%)",
|
|
// Dark elevation tiers — lightness INCREASES with elevation. See global.css.
|
|
surface1: "hsl(0 0% 8%)",
|
|
surface2: "hsl(0 0% 19%)",
|
|
},
|
|
};
|
|
|
|
export const NAV_THEME: Record<"light" | "dark", Theme> = {
|
|
light: {
|
|
...DefaultTheme,
|
|
colors: {
|
|
background: THEME.light.background,
|
|
border: THEME.light.border,
|
|
card: THEME.light.card,
|
|
notification: THEME.light.destructive,
|
|
primary: THEME.light.primary,
|
|
text: THEME.light.foreground,
|
|
},
|
|
},
|
|
dark: {
|
|
...DarkTheme,
|
|
colors: {
|
|
background: THEME.dark.background,
|
|
border: THEME.dark.border,
|
|
card: THEME.dark.card,
|
|
notification: THEME.dark.destructive,
|
|
primary: THEME.dark.primary,
|
|
text: THEME.dark.foreground,
|
|
},
|
|
},
|
|
};
|