@restnfeel/agentc-starter-kit
Version:
한국어 기업용 CMS 모듈 - Task Master AI와 함께 빠르게 웹사이트를 구현할 수 있는 재사용 가능한 컴포넌트 시스템
279 lines (262 loc) • 7.92 kB
text/typescript
export interface ChatbotTheme {
primary: string;
secondary: string;
background: string;
surface: string;
text: {
primary: string;
secondary: string;
inverse: string;
};
border: string;
shadow: string;
success: string;
warning: string;
error: string;
radius: {
sm: string;
md: string;
lg: string;
full: string;
};
spacing: {
xs: string;
sm: string;
md: string;
lg: string;
xl: string;
};
typography: {
fontFamily: string;
fontSize: {
xs: string;
sm: string;
md: string;
lg: string;
xl: string;
};
fontWeight: {
normal: string;
medium: string;
semibold: string;
bold: string;
};
};
}
export const defaultTheme: ChatbotTheme = {
primary: "#3B82F6",
secondary: "#64748B",
background: "#FFFFFF",
surface: "#F8FAFC",
text: {
primary: "#1F2937",
secondary: "#6B7280",
inverse: "#FFFFFF",
},
border: "#E5E7EB",
shadow: "0 10px 15px -3px rgba(0, 0, 0, 0.1)",
success: "#10B981",
warning: "#F59E0B",
error: "#EF4444",
radius: {
sm: "0.375rem",
md: "0.5rem",
lg: "0.75rem",
full: "9999px",
},
spacing: {
xs: "0.25rem",
sm: "0.5rem",
md: "1rem",
lg: "1.5rem",
xl: "2rem",
},
typography: {
fontFamily:
'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
fontSize: {
xs: "0.75rem",
sm: "0.875rem",
md: "1rem",
lg: "1.125rem",
xl: "1.25rem",
},
fontWeight: {
normal: "400",
medium: "500",
semibold: "600",
bold: "700",
},
},
};
export const darkTheme: ChatbotTheme = {
primary: "#60A5FA",
secondary: "#94A3B8",
background: "#1F2937",
surface: "#374151",
text: {
primary: "#F9FAFB",
secondary: "#D1D5DB",
inverse: "#1F2937",
},
border: "#4B5563",
shadow: "0 10px 15px -3px rgba(0, 0, 0, 0.3)",
success: "#34D399",
warning: "#FBBF24",
error: "#F87171",
radius: {
sm: "0.375rem",
md: "0.5rem",
lg: "0.75rem",
full: "9999px",
},
spacing: {
xs: "0.25rem",
sm: "0.5rem",
md: "1rem",
lg: "1.5rem",
xl: "2rem",
},
typography: {
fontFamily:
'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
fontSize: {
xs: "0.75rem",
sm: "0.875rem",
md: "1rem",
lg: "1.125rem",
xl: "1.25rem",
},
fontWeight: {
normal: "400",
medium: "500",
semibold: "600",
bold: "700",
},
},
};
export const compactTheme: ChatbotTheme = {
...defaultTheme,
spacing: {
xs: "0.125rem",
sm: "0.25rem",
md: "0.5rem",
lg: "0.75rem",
xl: "1rem",
},
typography: {
...defaultTheme.typography,
fontSize: {
xs: "0.625rem",
sm: "0.75rem",
md: "0.875rem",
lg: "1rem",
xl: "1.125rem",
},
},
};
export const themes = {
default: defaultTheme,
dark: darkTheme,
compact: compactTheme,
} as const;
export type ThemeName = keyof typeof themes;
// Theme utility functions
export function getThemeVariables(theme: ChatbotTheme): Record<string, string> {
return {
"--chatbot-primary": theme.primary,
"--chatbot-secondary": theme.secondary,
"--chatbot-background": theme.background,
"--chatbot-surface": theme.surface,
"--chatbot-text-primary": theme.text.primary,
"--chatbot-text-secondary": theme.text.secondary,
"--chatbot-text-inverse": theme.text.inverse,
"--chatbot-border": theme.border,
"--chatbot-shadow": theme.shadow,
"--chatbot-success": theme.success,
"--chatbot-warning": theme.warning,
"--chatbot-error": theme.error,
"--chatbot-radius-sm": theme.radius.sm,
"--chatbot-radius-md": theme.radius.md,
"--chatbot-radius-lg": theme.radius.lg,
"--chatbot-radius-full": theme.radius.full,
"--chatbot-spacing-xs": theme.spacing.xs,
"--chatbot-spacing-sm": theme.spacing.sm,
"--chatbot-spacing-md": theme.spacing.md,
"--chatbot-spacing-lg": theme.spacing.lg,
"--chatbot-spacing-xl": theme.spacing.xl,
"--chatbot-font-family": theme.typography.fontFamily,
"--chatbot-font-size-xs": theme.typography.fontSize.xs,
"--chatbot-font-size-sm": theme.typography.fontSize.sm,
"--chatbot-font-size-md": theme.typography.fontSize.md,
"--chatbot-font-size-lg": theme.typography.fontSize.lg,
"--chatbot-font-size-xl": theme.typography.fontSize.xl,
"--chatbot-font-weight-normal": theme.typography.fontWeight.normal,
"--chatbot-font-weight-medium": theme.typography.fontWeight.medium,
"--chatbot-font-weight-semibold": theme.typography.fontWeight.semibold,
"--chatbot-font-weight-bold": theme.typography.fontWeight.bold,
};
}
export function applyTheme(theme: ChatbotTheme, element?: HTMLElement): void {
const target = element || document.documentElement;
const variables = getThemeVariables(theme);
Object.entries(variables).forEach(([property, value]) => {
target.style.setProperty(property, value);
});
}
export function createThemeStylesheet(theme: ChatbotTheme): string {
const variables = getThemeVariables(theme);
const variableDeclarations = Object.entries(variables)
.map(([property, value]) => ` ${property}: ${value};`)
.join("\n");
return `:root {\n${variableDeclarations}\n}`;
}
// CSS class generator for theme-aware components
export function generateThemeClasses(theme: ChatbotTheme) {
return {
button: {
primary: `bg-[var(--chatbot-primary)] text-[var(--chatbot-text-inverse)] hover:opacity-90
border border-transparent rounded-[var(--chatbot-radius-md)]
px-[var(--chatbot-spacing-md)] py-[var(--chatbot-spacing-sm)]
font-[var(--chatbot-font-weight-medium)] text-[var(--chatbot-font-size-sm)]
transition-all duration-200 focus:ring-2 focus:ring-[var(--chatbot-primary)] focus:ring-opacity-50`,
secondary: `bg-[var(--chatbot-surface)] text-[var(--chatbot-text-primary)] hover:bg-[var(--chatbot-border)]
border border-[var(--chatbot-border)] rounded-[var(--chatbot-radius-md)]
px-[var(--chatbot-spacing-md)] py-[var(--chatbot-spacing-sm)]
font-[var(--chatbot-font-weight-medium)] text-[var(--chatbot-font-size-sm)]
transition-all duration-200 focus:ring-2 focus:ring-[var(--chatbot-primary)] focus:ring-opacity-50`,
},
input: `bg-[var(--chatbot-background)] text-[var(--chatbot-text-primary)]
border border-[var(--chatbot-border)] rounded-[var(--chatbot-radius-md)]
px-[var(--chatbot-spacing-md)] py-[var(--chatbot-spacing-sm)]
text-[var(--chatbot-font-size-sm)] font-[var(--chatbot-font-weight-normal)]
focus:border-[var(--chatbot-primary)] focus:ring-2 focus:ring-[var(--chatbot-primary)] focus:ring-opacity-50
placeholder:text-[var(--chatbot-text-secondary)]`,
surface: `bg-[var(--chatbot-surface)] border border-[var(--chatbot-border)]
rounded-[var(--chatbot-radius-lg)] shadow-[var(--chatbot-shadow)]`,
text: {
primary: "text-[var(--chatbot-text-primary)]",
secondary: "text-[var(--chatbot-text-secondary)]",
inverse: "text-[var(--chatbot-text-inverse)]",
},
};
}
// React hook for theme management
export function useTheme(initialTheme: ThemeName = "default") {
const [currentTheme, setCurrentTheme] =
React.useState<ThemeName>(initialTheme);
React.useEffect(() => {
applyTheme(themes[currentTheme]);
}, [currentTheme]);
const changeTheme = React.useCallback((themeName: ThemeName) => {
setCurrentTheme(themeName);
}, []);
return {
currentTheme,
theme: themes[currentTheme],
changeTheme,
availableThemes: Object.keys(themes) as ThemeName[],
};
}
// Add React import for the hook
import React from "react";