Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ vi.mock("phosphor-react-native", () => ({
vi.mock("@/lib/theme", () => ({
useThemeColors: () => ({
gray: { 11: "#444444" },
status: { success: "#00aa00", error: "#cc0000" },
status: { success: "#00aa00", error: "#cc0000", merged: "#8800aa" },
}),
toRgba: (hex: string, alpha: number) => `${hex}/${alpha}`,
}));
Expand Down
41 changes: 18 additions & 23 deletions apps/mobile/src/features/tasks/components/PrStatusBadge.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { getPrVisualConfig } from "@posthog/core/git-interaction/prStatus";
import { GitMerge, GitPullRequest } from "phosphor-react-native";
import { Pressable } from "react-native";
import { openExternalUrl } from "@/lib/openExternalUrl";
Expand All @@ -13,11 +14,6 @@ interface PrStatusBadgeProps {
size?: "sm" | "md";
}

// Mirrors the desktop "merged" PR color (Radix purple-9 family). Theme tokens
// don't include a purple, and merged-PR purple is recognisable enough that a
// fixed value works in both light and dark.
const MERGED_COLOR = "#8e4ec6";

export function PrStatusBadge({
prUrl,
hideWhenUnresolved = false,
Expand All @@ -32,24 +28,23 @@ export function PrStatusBadge({
openExternalUrl(prUrl);
};

let color: string = themeColors.gray[11];
let Icon: typeof GitPullRequest = GitPullRequest;
let label = "Open PR";

if (status?.merged) {
color = MERGED_COLOR;
Icon = GitMerge;
label = "Open merged PR";
} else if (status?.state === "closed") {
color = themeColors.status.error;
label = "Open closed PR";
} else if (status?.draft) {
color = themeColors.gray[11];
label = "Open draft PR";
} else if (status?.state === "open") {
color = themeColors.status.success;
label = "Open PR";
}
const visual = getPrVisualConfig(
status?.state ?? "open",
status?.merged ?? false,
status?.draft ?? false,
);
const colorByTone = {
gray: themeColors.gray[11],
green: themeColors.status.success,
red: themeColors.status.error,
purple: themeColors.status.merged,
} satisfies Record<typeof visual.color, string>;
const color = colorByTone[visual.color];
const Icon = visual.icon === "merged" ? GitMerge : GitPullRequest;
const label =
visual.label === "Open"
? "Open PR"
: `Open ${visual.label.toLowerCase()} PR`;

const box = size === "sm" ? "h-7 w-7" : "h-9 w-9";
const iconSize = size === "sm" ? 16 : 20;
Expand Down
103 changes: 8 additions & 95 deletions apps/mobile/src/lib/theme.ts
Original file line number Diff line number Diff line change
@@ -1,95 +1,6 @@
import { APP_COLOR_PALETTE, type AppThemeColors } from "@posthog/shared/theme";
import { useColorScheme, vars } from "nativewind";

/**
* Single source of truth for all theme colors.
* Defined as hex for readability, converted to RGB for NativeWind vars().
*/
// Color palette mirrored from the desktop app (apps/code globals.css).
// Light: slate gray + orange accent. Dark: slate gray + yellow accent.
const colors = {
light: {
gray: {
1: "#f2f3ee",
2: "#eceee8",
3: "#e4e5de",
4: "#d8dbd1",
5: "#cbd0c3",
6: "#bcc1b4",
7: "#a9af9f",
8: "#93998a",
9: "#6b7165",
10: "#5a6054",
11: "#3a4036",
12: "#0d0d0d",
},
accent: {
1: "#fff5f0",
2: "#ffe8dc",
3: "#ffd0b8",
4: "#ffb38a",
5: "#ff8f56",
6: "#f57030",
7: "#e05a18",
8: "#c94800",
9: "#f54d00",
10: "#e64600",
11: "#a33300",
12: "#4d1800",
contrast: "#ffffff",
},
status: {
success: "#16a34a",
error: "#dc2626",
warning: "#d97706",
info: "#2563eb",
},
background: "#f2f3ee",
// "Card" surface — used for raised UI like buttons, composer card, pills.
// Pure white in light mode for max contrast against the cream background;
// gray-3 in dark mode so cards lift slightly off the bg.
card: "#ffffff",
},
dark: {
gray: {
1: "#131316",
2: "#18181f",
3: "#1e1e28",
4: "#24243e",
5: "#2a2a37",
6: "#2e2e3d",
7: "#40405a",
8: "#616180",
9: "#7c7c9e",
10: "#8d8daa",
11: "#9898b6",
12: "#e6e6e6",
},
accent: {
1: "#14120a",
2: "#1a1608",
3: "#261e07",
4: "#362900",
5: "#443300",
6: "#524007",
7: "#6b561a",
8: "#8c7230",
9: "#f8be2a",
10: "#ebb520",
11: "#fcc84e",
12: "#fde8b8",
contrast: "#1a1200",
},
status: {
success: "#4ade80",
error: "#f87171",
warning: "#fbbf24",
info: "#60a5fa",
},
background: "#131316",
card: "#1e1e28",
},
} as const;

// Convert hex to RGB space-separated format for NativeWind vars()
function hexToRgb(hex: string): string {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
Expand All @@ -108,7 +19,7 @@ function hexToRgba(hex: string, alpha: number): string {
}

// Generate NativeWind vars() from color definitions
function createThemeVars(theme: (typeof colors)["light" | "dark"]) {
function createThemeVars(theme: AppThemeColors) {
return vars({
"--gray-1": hexToRgb(theme.gray[1]),
"--gray-2": hexToRgb(theme.gray[2]),
Expand Down Expand Up @@ -145,11 +56,11 @@ function createThemeVars(theme: (typeof colors)["light" | "dark"]) {
}

// NativeWind vars() for runtime theming (used in root View style)
export const lightTheme = createThemeVars(colors.light);
export const darkTheme = createThemeVars(colors.dark);
export const lightTheme = createThemeVars(APP_COLOR_PALETTE.light);
export const darkTheme = createThemeVars(APP_COLOR_PALETTE.dark);

// Types
export type ThemeColors = (typeof colors)["light" | "dark"];
export type ThemeColors = AppThemeColors;

/**
* Hook to get raw hex color values for native components.
Expand All @@ -162,7 +73,9 @@ export type ThemeColors = (typeof colors)["light" | "dark"];
*/
export function useThemeColors(): ThemeColors {
const { colorScheme } = useColorScheme();
return colorScheme === "dark" ? colors.dark : colors.light;
return colorScheme === "dark"
? APP_COLOR_PALETTE.dark
: APP_COLOR_PALETTE.light;
}

/**
Expand Down
4 changes: 4 additions & 0 deletions packages/shared/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@
"./posthog-property-headers": {
"types": "./dist/posthog-property-headers.d.ts",
"import": "./dist/posthog-property-headers.js"
},
"./theme": {
"types": "./dist/theme.d.ts",
"import": "./dist/theme.js"
}
},
"scripts": {
Expand Down
93 changes: 93 additions & 0 deletions packages/shared/src/theme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
export const APP_COLOR_PALETTE = {
light: {
gray: {
1: "#f2f3ee",
2: "#eceee8",
3: "#e4e5de",
4: "#d8dbd1",
5: "#cbd0c3",
6: "#bcc1b4",
7: "#a9af9f",
8: "#93998a",
9: "#6b7165",
10: "#5a6054",
11: "#3a4036",
12: "#0d0d0d",
},
accent: {
1: "#fff5f0",
2: "#ffe8dc",
3: "#ffd0b8",
4: "#ffb38a",
5: "#ff8f56",
6: "#f57030",
7: "#e05a18",
8: "#c94800",
9: "#f54d00",
10: "#e64600",
11: "#a33300",
12: "#4d1800",
contrast: "#ffffff",
},
status: {
success: "#16a34a",
error: "#dc2626",
warning: "#d97706",
info: "#2563eb",
merged: "#8e4ec6",
},
background: "#f2f3ee",
card: "#ffffff",
},
dark: {
gray: {
1: "#131316",
2: "#18181f",
3: "#1e1e28",
4: "#24243e",
5: "#2a2a37",
6: "#2e2e3d",
7: "#40405a",
8: "#616180",
9: "#7c7c9e",
10: "#8d8daa",
11: "#9898b6",
12: "#e6e6e6",
},
accent: {
1: "#14120a",
2: "#1a1608",
3: "#261e07",
4: "#362900",
5: "#443300",
6: "#524007",
7: "#6b561a",
8: "#8c7230",
9: "#f8be2a",
10: "#ebb520",
11: "#fcc84e",
12: "#fde8b8",
contrast: "#1a1200",
},
status: {
success: "#4ade80",
error: "#f87171",
warning: "#fbbf24",
info: "#60a5fa",
merged: "#8e4ec6",
},
background: "#131316",
card: "#1e1e28",
},
} as const;

export type AppThemeColors =
(typeof APP_COLOR_PALETTE)[keyof typeof APP_COLOR_PALETTE];

export const POSTHOG_BRAND_COLORS = [
APP_COLOR_PALETTE.light.accent[9],
APP_COLOR_PALETTE.dark.accent[9],
"#1d4aff",
"#000000",
"#ffffff",
] as const;
1 change: 1 addition & 0 deletions packages/shared/tsup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export default defineConfig({
"src/domain-types.ts",
"src/mcp-sandbox-proxy.ts",
"src/posthog-property-headers.ts",
"src/theme.ts",
"src/types.ts",
],
format: ["esm"],
Expand Down
37 changes: 20 additions & 17 deletions packages/ui/src/features/code-editor/theme/editorTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { HighlightStyle, syntaxHighlighting } from "@codemirror/language";
import type { Extension } from "@codemirror/state";
import { EditorView } from "@codemirror/view";
import { tags as t } from "@lezer/highlight";
import { APP_COLOR_PALETTE } from "@posthog/shared/theme";

function withAlpha(hex: string, alpha: number): string {
const a = Math.round(alpha * 255)
Expand All @@ -15,40 +16,42 @@ const dark = {
chalky: "#e5c07b",
coral: "#e06c75",
cyan: "#56b6c2",
invalid: "#e6e6e6",
ivory: "#e6e6e6",
stone: "#9898b6",
invalid: APP_COLOR_PALETTE.dark.gray[12],
ivory: APP_COLOR_PALETTE.dark.gray[12],
stone: APP_COLOR_PALETTE.dark.gray[11],
malibu: "#61afef",
sage: "#98c379",
whiskey: "#d19a66",
violet: "#c678dd",
background: "#131316",
highlightBackground: "#1e1e28",
tooltipBackground: "#24243e",
selection: "#2e2e3d",
cursor: "#f8be2a",
background: APP_COLOR_PALETTE.dark.background,
highlightBackground: APP_COLOR_PALETTE.dark.gray[3],
tooltipBackground: APP_COLOR_PALETTE.dark.gray[4],
selection: APP_COLOR_PALETTE.dark.gray[6],
cursor: APP_COLOR_PALETTE.dark.accent[9],
};

type EditorColors = { [K in keyof typeof dark]: string };

// PostHog-inspired light theme colors
const light = {
chalky: "#c18401",
coral: "#c45649",
cyan: "#0184bc",
invalid: "#0d0d0d",
invalid: APP_COLOR_PALETTE.light.gray[12],
ivory: "#1a1d17",
stone: "#6b7165",
stone: APP_COLOR_PALETTE.light.gray[9],
malibu: "#4078f2",
sage: "#50a14f",
whiskey: "#986801",
violet: "#a626a4",
background: "#f2f3ee",
highlightBackground: "#e4e5de",
tooltipBackground: "#eceee8",
selection: "#d8dbd1",
cursor: "#f54d00",
background: APP_COLOR_PALETTE.light.background,
highlightBackground: APP_COLOR_PALETTE.light.gray[3],
tooltipBackground: APP_COLOR_PALETTE.light.gray[2],
selection: APP_COLOR_PALETTE.light.gray[4],
cursor: APP_COLOR_PALETTE.light.accent[9],
};

function createEditorTheme(colors: typeof dark, isDark: boolean) {
function createEditorTheme(colors: EditorColors, isDark: boolean) {
return EditorView.theme(
{
"&": {
Expand Down Expand Up @@ -184,7 +187,7 @@ function createEditorTheme(colors: typeof dark, isDark: boolean) {
);
}

function createHighlightStyle(colors: typeof dark) {
function createHighlightStyle(colors: EditorColors) {
return HighlightStyle.define([
{ tag: t.keyword, color: colors.violet },
{
Expand Down
Loading
Loading