Standardize Foundry frontend colors with semantic design tokens (#241)

Extract hardcoded colors from 15+ component files into a centralized
token system (tokens.ts + shared-styles.ts) so all UI colors flow
through FoundryTokens. This eliminates 160+ scattered color values
and makes light mode a single-file change in the future.

- Add FoundryTokens interface with dark/light variants
- Add shared style helpers (buttons, cards, inputs, badges)
- Bridge CSS custom properties for styles.css theme support
- Add useFoundryTokens() hook and ColorMode context
- Migrate all mock-layout/* and mock-onboarding components

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Nicholas Kissel 2026-03-11 20:52:06 -07:00 committed by GitHub
parent ed6e6f6fa5
commit f09b9090bb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 887 additions and 523 deletions

View file

@ -1,6 +1,13 @@
import { createDarkTheme, type Theme } from "baseui";
import { createContext, useContext } from "react";
import { createDarkTheme, createLightTheme, type Theme } from "baseui";
import { useStyletron } from "baseui";
import { getFoundryTokens, type FoundryTokens } from "../styles/tokens";
export const appTheme: Theme = createDarkTheme({
const STORAGE_KEY = "sandbox-agent-foundry:color-mode";
export type ColorMode = "dark" | "light";
export const darkTheme: Theme = createDarkTheme({
colors: {
primary: "#e4e4e7", // zinc-200
accent: "#ff4f00", // orange accent (inspector)
@ -16,3 +23,60 @@ export const appTheme: Theme = createDarkTheme({
borderTransparent: "rgba(255, 255, 255, 0.07)", // inspector --border-2
},
});
export const lightTheme: Theme = createLightTheme({
colors: {
primary: "#27272a", // zinc-800
accent: "#ff4f00", // orange accent (inspector)
backgroundPrimary: "#ffffff",
backgroundSecondary: "#f4f4f5", // zinc-100
backgroundTertiary: "#fafafa", // zinc-50
backgroundInversePrimary: "#18181b",
contentPrimary: "#09090b", // zinc-950
contentSecondary: "#52525b", // zinc-600
contentTertiary: "#a1a1aa", // zinc-400
contentInversePrimary: "#ffffff",
borderOpaque: "rgba(0, 0, 0, 0.10)",
borderTransparent: "rgba(0, 0, 0, 0.06)",
},
});
/** Kept for backwards compat — defaults to dark */
export const appTheme = darkTheme;
export interface ColorModeContext {
colorMode: ColorMode;
setColorMode: (mode: ColorMode) => void;
}
export const ColorModeCtx = createContext<ColorModeContext>({
colorMode: "dark",
setColorMode: () => {},
});
export function useColorMode() {
return useContext(ColorModeCtx);
}
export function useFoundryTokens(): FoundryTokens {
const [, theme] = useStyletron();
return getFoundryTokens(theme);
}
export function getStoredColorMode(): ColorMode {
try {
const stored = localStorage.getItem(STORAGE_KEY);
if (stored === "light" || stored === "dark") return stored;
} catch {
// ignore
}
return "dark";
}
export function storeColorMode(mode: ColorMode) {
try {
localStorage.setItem(STORAGE_KEY, mode);
} catch {
// ignore
}
}