UNPKG

@jonmatum/react-mfe-shell

Version:

Production-ready React micro frontend shell with atomic design system, shared components, and utilities for building scalable MFE applications

1,920 lines (1,907 loc) 159 kB
"use client" // src/utils/tokens.ts var baseColors = { // Neutral colors white: "#ffffff", black: "#000000", // Gray scale (optimized for accessibility) gray: { 50: "#f9fafb", 100: "#f3f4f6", 200: "#e5e7eb", 300: "#d1d5db", 400: "#9ca3af", 500: "#6b7280", 600: "#4b5563", 700: "#374151", 800: "#1f2937", 900: "#111827", 950: "#030712" }, // Primary brand colors blue: { 50: "#eff6ff", 100: "#dbeafe", 200: "#bfdbfe", 300: "#93c5fd", 400: "#60a5fa", 500: "#3b82f6", 600: "#2563eb", 700: "#1d4ed8", 800: "#1e40af", 900: "#1e3a8a", 950: "#172554" }, // Success colors green: { 50: "#f0fdf4", 100: "#dcfce7", 200: "#bbf7d0", 300: "#86efac", 400: "#4ade80", 500: "#22c55e", 600: "#16a34a", 700: "#15803d", 800: "#166534", 900: "#14532d", 950: "#052e16" }, // Warning colors yellow: { 50: "#fefce8", 100: "#fef3c7", 200: "#fde68a", 300: "#fcd34d", 400: "#fbbf24", 500: "#f59e0b", 600: "#d97706", 700: "#b45309", 800: "#92400e", 900: "#78350f", 950: "#451a03" }, // Error colors red: { 50: "#fef2f2", 100: "#fee2e2", 200: "#fecaca", 300: "#fca5a5", 400: "#f87171", 500: "#ef4444", 600: "#dc2626", 700: "#b91c1c", 800: "#991b1b", 900: "#7f1d1d", 950: "#450a0a" }, // Info colors cyan: { 50: "#ecfeff", 100: "#cffafe", 200: "#a5f3fc", 300: "#67e8f9", 400: "#22d3ee", 500: "#06b6d4", 600: "#0891b2", 700: "#0e7490", 800: "#155e75", 900: "#164e63", 950: "#083344" } }; var semanticColors = { primary: baseColors.blue, secondary: baseColors.gray, success: baseColors.green, warning: baseColors.yellow, error: baseColors.red, info: baseColors.cyan }; var themeColors = { light: { // Background colors background: { primary: baseColors.white, secondary: baseColors.gray[50], tertiary: baseColors.gray[100] }, // Surface colors (cards, modals, etc.) surface: { primary: baseColors.white, secondary: baseColors.gray[50], tertiary: baseColors.gray[100], elevated: baseColors.white }, // Text colors text: { primary: baseColors.gray[900], secondary: baseColors.gray[600], tertiary: baseColors.gray[500], inverse: baseColors.white, disabled: baseColors.gray[400] }, // Border colors border: { primary: baseColors.gray[200], secondary: baseColors.gray[300], tertiary: baseColors.gray[400], focus: semanticColors.primary[500] }, // Interactive colors interactive: { primary: semanticColors.primary[600], "primary-hover": semanticColors.primary[700], "primary-active": semanticColors.primary[800], secondary: baseColors.gray[100], "secondary-hover": baseColors.gray[200], "secondary-active": baseColors.gray[300] }, // Status colors status: { success: semanticColors.success[600], warning: semanticColors.warning[600], error: semanticColors.error[600], info: semanticColors.info[600] } }, dark: { // Background colors background: { primary: baseColors.gray[900], secondary: baseColors.gray[800], tertiary: baseColors.gray[700] }, // Surface colors surface: { primary: baseColors.gray[800], secondary: baseColors.gray[700], tertiary: baseColors.gray[600], elevated: baseColors.gray[700] }, // Text colors text: { primary: baseColors.gray[100], secondary: baseColors.gray[300], tertiary: baseColors.gray[400], inverse: baseColors.gray[900], disabled: baseColors.gray[500] }, // Border colors border: { primary: baseColors.gray[700], secondary: baseColors.gray[600], tertiary: baseColors.gray[500], focus: semanticColors.primary[400] }, // Interactive colors interactive: { primary: semanticColors.primary[500], "primary-hover": semanticColors.primary[400], "primary-active": semanticColors.primary[300], secondary: baseColors.gray[700], "secondary-hover": baseColors.gray[600], "secondary-active": baseColors.gray[500] }, // Status colors status: { success: semanticColors.success[500], warning: semanticColors.warning[500], error: semanticColors.error[500], info: semanticColors.info[500] } } }; var fontFamily = { sans: [ "Inter", "-apple-system", "BlinkMacSystemFont", '"Segoe UI"', "Roboto", '"Helvetica Neue"', "Arial", "sans-serif" ], mono: [ '"JetBrains Mono"', "Menlo", "Monaco", "Consolas", '"Liberation Mono"', '"Courier New"', "monospace" ] }; var fontSize = { xs: ["0.75rem", { lineHeight: "1rem" }], // 12px sm: ["0.875rem", { lineHeight: "1.25rem" }], // 14px base: ["1rem", { lineHeight: "1.5rem" }], // 16px lg: ["1.125rem", { lineHeight: "1.75rem" }], // 18px xl: ["1.25rem", { lineHeight: "1.75rem" }], // 20px "2xl": ["1.5rem", { lineHeight: "2rem" }], // 24px "3xl": ["1.875rem", { lineHeight: "2.25rem" }], // 30px "4xl": ["2.25rem", { lineHeight: "2.5rem" }], // 36px "5xl": ["3rem", { lineHeight: "1" }], // 48px "6xl": ["3.75rem", { lineHeight: "1" }], // 60px "7xl": ["4.5rem", { lineHeight: "1" }], // 72px "8xl": ["6rem", { lineHeight: "1" }], // 96px "9xl": ["8rem", { lineHeight: "1" }] // 128px }; var fontWeight = { thin: "100", extralight: "200", light: "300", normal: "400", medium: "500", semibold: "600", bold: "700", extrabold: "800", black: "900" }; var letterSpacing = { tighter: "-0.05em", tight: "-0.025em", normal: "0em", wide: "0.025em", wider: "0.05em", widest: "0.1em" }; var spacing = { 0: "0px", px: "1px", 0.5: "0.125rem", // 2px 1: "0.25rem", // 4px 1.5: "0.375rem", // 6px 2: "0.5rem", // 8px 2.5: "0.625rem", // 10px 3: "0.75rem", // 12px 3.5: "0.875rem", // 14px 4: "1rem", // 16px 5: "1.25rem", // 20px 6: "1.5rem", // 24px 7: "1.75rem", // 28px 8: "2rem", // 32px 9: "2.25rem", // 36px 10: "2.5rem", // 40px 11: "2.75rem", // 44px 12: "3rem", // 48px 14: "3.5rem", // 56px 16: "4rem", // 64px 20: "5rem", // 80px 24: "6rem", // 96px 28: "7rem", // 112px 32: "8rem", // 128px 36: "9rem", // 144px 40: "10rem", // 160px 44: "11rem", // 176px 48: "12rem", // 192px 52: "13rem", // 208px 56: "14rem", // 224px 60: "15rem", // 240px 64: "16rem", // 256px 72: "18rem", // 288px 80: "20rem", // 320px 96: "24rem" // 384px }; var boxShadow = { none: "none", sm: "0 1px 2px 0 rgb(0 0 0 / 0.05)", base: "0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)", md: "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)", lg: "0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)", xl: "0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)", "2xl": "0 25px 50px -12px rgb(0 0 0 / 0.25)", inner: "inset 0 2px 4px 0 rgb(0 0 0 / 0.05)" }; var dropShadow = { none: "none", sm: "0 1px 1px rgb(0 0 0 / 0.05)", base: "0 1px 2px rgb(0 0 0 / 0.1)", md: "0 4px 3px rgb(0 0 0 / 0.07)", lg: "0 10px 8px rgb(0 0 0 / 0.04)", xl: "0 20px 13px rgb(0 0 0 / 0.03)", "2xl": "0 25px 25px rgb(0 0 0 / 0.15)" }; var borderRadius = { none: "0px", sm: "0.125rem", // 2px base: "0.25rem", // 4px md: "0.375rem", // 6px lg: "0.5rem", // 8px xl: "0.75rem", // 12px "2xl": "1rem", // 16px "3xl": "1.5rem", // 24px full: "9999px" }; var breakpoints = { xs: "475px", sm: "640px", md: "768px", lg: "1024px", xl: "1280px", "2xl": "1536px" }; var animationDuration = { 75: "75ms", 100: "100ms", 150: "150ms", 200: "200ms", 300: "300ms", 500: "500ms", 700: "700ms", 1e3: "1000ms" }; var animationTimingFunction = { linear: "linear", in: "cubic-bezier(0.4, 0, 1, 1)", out: "cubic-bezier(0, 0, 0.2, 1)", "in-out": "cubic-bezier(0.4, 0, 0.2, 1)" }; var zIndex = { auto: "auto", 0: "0", 10: "10", 20: "20", 30: "30", 40: "40", 50: "50", dropdown: "1000", sticky: "1020", fixed: "1030", modal: "1040", popover: "1050", tooltip: "1060", toast: "1070" }; var componentSizes = { xs: { height: spacing[6], padding: `${spacing[1]} ${spacing[2]}`, fontSize: fontSize.xs[0] }, sm: { height: spacing[8], padding: `${spacing[1.5]} ${spacing[3]}`, fontSize: fontSize.sm[0] }, md: { height: spacing[10], padding: `${spacing[2]} ${spacing[4]}`, fontSize: fontSize.base[0] }, lg: { height: spacing[12], padding: `${spacing[2.5]} ${spacing[6]}`, fontSize: fontSize.lg[0] }, xl: { height: spacing[14], padding: `${spacing[3]} ${spacing[8]}`, fontSize: fontSize.xl[0] } }; var tokens = { colors: { base: baseColors, semantic: semanticColors, theme: themeColors }, typography: { fontFamily, fontSize, fontWeight, letterSpacing }, spacing, shadows: { box: boxShadow, drop: dropShadow }, borderRadius, breakpoints, animation: { duration: animationDuration, timingFunction: animationTimingFunction }, zIndex, components: { sizes: componentSizes } }; var colors = baseColors; var typography = { fontFamily, fontSize, fontWeight, letterSpacing }; var shadows = boxShadow; var transitions = animationTimingFunction; var tokens_default = tokens; // src/utils/theme.ts var THEME_MODES = ["light", "dark", "system"]; var THEME_STORAGE_KEY = "mfe-shell-theme"; var THEME_CLASS_NAMES = { light: "", dark: "dark" }; var DARK_MODE_MEDIA_QUERY = "(prefers-color-scheme: dark)"; function getSystemTheme() { if (typeof window === "undefined") { return "light"; } try { const mediaQuery = window.matchMedia(DARK_MODE_MEDIA_QUERY); return mediaQuery.matches ? "dark" : "light"; } catch { return "light"; } } function resolveTheme(mode) { switch (mode) { case "light": return "light"; case "dark": return "dark"; case "system": return getSystemTheme(); default: return "light"; } } function saveThemePreference(mode) { try { localStorage.setItem(THEME_STORAGE_KEY, mode); } catch (error) { console.warn("Failed to save theme preference:", error); } } function loadThemePreference() { try { const stored = localStorage.getItem(THEME_STORAGE_KEY); if (stored && THEME_MODES.includes(stored)) { return stored; } } catch (error) { console.warn("Failed to load theme preference:", error); } return "system"; } function clearThemePreference() { try { localStorage.removeItem(THEME_STORAGE_KEY); } catch (error) { console.warn("Failed to clear theme preference:", error); } } function applyThemeToDOM(mode) { if (typeof document === "undefined") { return; } const resolvedTheme = resolveTheme(mode); const { classList } = document.documentElement; classList.remove("light", "dark"); if (resolvedTheme === "dark") { classList.add("dark"); } document.documentElement.setAttribute("data-theme", resolvedTheme); } function getCurrentThemeFromDOM() { if (typeof document === "undefined") { return "light"; } return document.documentElement.classList.contains("dark") ? "dark" : "light"; } function generateThemeCustomProperties(theme2) { return { // Background colors "--color-background-primary": theme2.background.primary, "--color-background-secondary": theme2.background.secondary, "--color-background-tertiary": theme2.background.tertiary, // Surface colors "--color-surface-primary": theme2.surface.primary, "--color-surface-secondary": theme2.surface.secondary, "--color-surface-tertiary": theme2.surface.tertiary, "--color-surface-elevated": theme2.surface.elevated || theme2.surface.primary, // Text colors "--color-text-primary": theme2.text.primary, "--color-text-secondary": theme2.text.secondary, "--color-text-tertiary": theme2.text.tertiary, "--color-text-inverse": theme2.text.inverse, "--color-text-disabled": theme2.text.disabled, // Border colors "--color-border-primary": theme2.border.primary, "--color-border-secondary": theme2.border.secondary, "--color-border-tertiary": theme2.border.tertiary, "--color-border-focus": theme2.border.focus, // Interactive colors "--color-interactive-primary": theme2.interactive.primary, "--color-interactive-primary-hover": theme2.interactive["primary-hover"], "--color-interactive-primary-active": theme2.interactive["primary-active"], "--color-interactive-secondary": theme2.interactive.secondary, "--color-interactive-secondary-hover": theme2.interactive["secondary-hover"], "--color-interactive-secondary-active": theme2.interactive["secondary-active"], // Status colors "--color-status-success": theme2.status.success, "--color-status-warning": theme2.status.warning, "--color-status-error": theme2.status.error, "--color-status-info": theme2.status.info }; } function applyThemeCustomProperties(theme2) { if (typeof document === "undefined") { return; } const properties = generateThemeCustomProperties(theme2); const { style } = document.documentElement; Object.entries(properties).forEach(([property, value]) => { style.setProperty(property, value); }); } function createSystemThemeListener(callback) { if (typeof window === "undefined") { return null; } try { const mediaQuery = window.matchMedia(DARK_MODE_MEDIA_QUERY); const listener = (event) => { callback(event.matches); }; if (mediaQuery.addEventListener) { mediaQuery.addEventListener("change", listener); return () => { mediaQuery.removeEventListener("change", listener); }; } else if (mediaQuery.addListener) { mediaQuery.addListener(listener); return () => { mediaQuery.removeListener(listener); }; } } catch (error) { console.warn("Failed to create system theme listener:", error); } return null; } function createThemeConfig(mode) { const resolvedTheme = resolveTheme(mode); const colors2 = themeColors[resolvedTheme]; return { mode, colors: colors2 }; } function isValidThemeMode(mode) { return THEME_MODES.includes(mode); } function getOppositeTheme(theme2) { return theme2 === "light" ? "dark" : "light"; } function isDarkTheme(mode) { return resolveTheme(mode) === "dark"; } function isLightTheme(mode) { return resolveTheme(mode) === "light"; } function getThemeColor(colorPath, mode = "system") { const resolvedTheme = resolveTheme(mode); const colors2 = themeColors[resolvedTheme]; const pathParts = colorPath.split("."); let value = colors2; for (const part of pathParts) { if (value && typeof value === "object" && part in value) { value = value[part]; } else { return ""; } } return typeof value === "string" ? value : ""; } function initializeTheme() { const preferredMode = loadThemePreference(); applyThemeToDOM(preferredMode); const resolvedTheme = resolveTheme(preferredMode); applyThemeCustomProperties(themeColors[resolvedTheme]); return preferredMode; } function setupThemeManagement(onThemeChange) { const currentMode = initializeTheme(); const cleanup = createSystemThemeListener((isDark) => { const storedMode = loadThemePreference(); if (storedMode === "system") { const newTheme = isDark ? "dark" : "light"; applyThemeToDOM("system"); applyThemeCustomProperties(themeColors[newTheme]); onThemeChange?.("system", newTheme); } }); const setTheme = (mode) => { saveThemePreference(mode); applyThemeToDOM(mode); const resolvedTheme = resolveTheme(mode); applyThemeCustomProperties(themeColors[resolvedTheme]); onThemeChange?.(mode, resolvedTheme); }; return { currentMode, setTheme, cleanup: cleanup || (() => { }) }; } // src/utils/form.ts import { useId, useState, useCallback } from "react"; var validateField = (value, rules) => { if (!rules) return void 0; if (rules.required) { const isEmpty = value === void 0 || value === null || value === "" || Array.isArray(value) && value.length === 0; if (isEmpty) { return typeof rules.required === "string" ? rules.required : "This field is required"; } } if (!value && !rules.required) return void 0; if (typeof value === "string") { if (rules.minLength) { const minLength = typeof rules.minLength === "number" ? rules.minLength : rules.minLength.value; const message = typeof rules.minLength === "object" ? rules.minLength.message : `Must be at least ${minLength} characters`; if (value.length < minLength) return message; } if (rules.maxLength) { const maxLength = typeof rules.maxLength === "number" ? rules.maxLength : rules.maxLength.value; const message = typeof rules.maxLength === "object" ? rules.maxLength.message : `Must be no more than ${maxLength} characters`; if (value.length > maxLength) return message; } if (rules.pattern) { const pattern = rules.pattern instanceof RegExp ? rules.pattern : rules.pattern.value; const message = rules.pattern instanceof RegExp ? "Invalid format" : rules.pattern.message; if (!pattern.test(value)) return message; } } if (rules.custom) { return rules.custom(value); } return void 0; }; var useFormField = ({ initialValue = "", validation, validateOnChange = false, validateOnBlur = true } = {}) => { const [state, setState] = useState({ value: initialValue, error: void 0, touched: false, dirty: false }); const validate = useCallback( (value) => { return validateField(value, validation); }, [validation] ); const setValue = useCallback( (newValue) => { setState((prev) => { const error = validateOnChange ? validate(newValue) : prev.error; return { ...prev, value: newValue, dirty: newValue !== initialValue, error }; }); }, [initialValue, validate, validateOnChange] ); const setTouched = useCallback(() => { setState((prev) => { if (prev.touched) return prev; const error = validateOnBlur ? validate(prev.value) : prev.error; return { ...prev, touched: true, error }; }); }, [validate, validateOnBlur]); const setError = useCallback((error) => { setState((prev) => ({ ...prev, error })); }, []); const reset = useCallback(() => { setState({ value: initialValue, error: void 0, touched: false, dirty: false }); }, [initialValue]); const validateNow = useCallback(() => { const error = validate(state.value); setState((prev) => ({ ...prev, error, touched: true })); return !error; }, [validate, state.value]); return { ...state, setValue, setTouched, setError, reset, validate: validateNow, isValid: !state.error }; }; var validationPatterns = { email: { value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, message: "Please enter a valid email address" }, phone: { value: /^[+]?[1-9][\d]{0,15}$/, message: "Please enter a valid phone number" }, url: { value: /^https?:\/\/.+/, message: "Please enter a valid URL" }, alphanumeric: { value: /^[a-zA-Z0-9]+$/, message: "Only letters and numbers are allowed" }, noSpecialChars: { value: /^[a-zA-Z0-9\s]+$/, message: "Special characters are not allowed" } }; var useFormFieldId = (prefix = "field") => { return useId() || `${prefix}-${Math.random().toString(36).substr(2, 9)}`; }; var getFieldErrorProps = (error, fieldId) => ({ "aria-invalid": error ? "true" : "false", "aria-describedby": error && fieldId ? `${fieldId}-error` : void 0 }); var getFormFieldAccessibility = (fieldId, error, description, required) => { const describedBy = []; if (description) describedBy.push(`${fieldId}-description`); if (error) describedBy.push(`${fieldId}-error`); return { id: fieldId, "aria-invalid": error ? "true" : "false", "aria-describedby": describedBy.length > 0 ? describedBy.join(" ") : void 0, "aria-required": required ? "true" : void 0 }; }; // src/utils/index.ts function classNames(...classes) { if (classes.length === 0) return ""; if (classes.length === 1) return classes[0] || ""; return classes.filter(Boolean).join(" "); } var storage = { get: (key, defaultValue) => { try { const item = localStorage.getItem(key); return item ? JSON.parse(item) : defaultValue; } catch { return defaultValue; } }, set: (key, value) => { try { localStorage.setItem(key, JSON.stringify(value)); } catch { } }, remove: (key) => { try { localStorage.removeItem(key); } catch { } }, clear: () => { try { localStorage.clear(); } catch { } } }; var systemThemeCache = null; var mediaQueryListener = null; var theme = { /** * Gets the system theme preference with caching */ getSystemTheme: () => { if (typeof window === "undefined") return "light"; if (systemThemeCache === null) { const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)"); systemThemeCache = mediaQuery.matches ? "dark" : "light"; if (!mediaQueryListener && mediaQuery.addEventListener) { mediaQueryListener = (e) => { systemThemeCache = e.matches ? "dark" : "light"; }; mediaQuery.addEventListener("change", mediaQueryListener); } } return systemThemeCache; }, /** * Applies theme to document with performance optimization */ applyTheme: (themeName) => { if (typeof document === "undefined") return; const actualTheme = themeName === "system" ? theme.getSystemTheme() : themeName; const { classList } = document.documentElement; if (classList && classList.contains) { const currentTheme = classList.contains("dark") ? "dark" : "light"; if (currentTheme !== actualTheme) { classList.remove("light", "dark"); classList.add(actualTheme); } } else { classList.remove("light", "dark"); classList.add(actualTheme); } }, /** * Clear theme cache (useful for testing) */ clearCache: () => { systemThemeCache = null; if (mediaQueryListener && typeof window !== "undefined") { const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)"); if (mediaQuery.removeEventListener) { mediaQuery.removeEventListener("change", mediaQueryListener); } mediaQueryListener = null; } } }; var debounceTimers = /* @__PURE__ */ new WeakMap(); function debounce(func, wait) { return (...args) => { const existingTimer = debounceTimers.get(func); if (existingTimer) { clearTimeout(existingTimer); } const timer = setTimeout(() => { debounceTimers.delete(func); func(...args); }, wait); debounceTimers.set(func, timer); }; } var throttleTimers = /* @__PURE__ */ new WeakMap(); function throttle(func, limit) { return (...args) => { const now = Date.now(); const throttleData = throttleTimers.get(func) || { timer: null, lastRun: 0 }; if (now - throttleData.lastRun >= limit) { func(...args); throttleData.lastRun = now; } else if (!throttleData.timer) { throttleData.timer = setTimeout( () => { func(...args); throttleData.lastRun = Date.now(); throttleData.timer = null; }, limit - (now - throttleData.lastRun) ); } throttleTimers.set(func, throttleData); }; } var idCounter = 0; function generateId(prefix = "id") { return `${prefix}-${++idCounter}-${Math.random().toString(36).substr(2, 9)}`; } var numberFormatters = /* @__PURE__ */ new Map(); function formatNumber(num, locale = "en-US") { if (!numberFormatters.has(locale)) { numberFormatters.set(locale, new Intl.NumberFormat(locale)); } return numberFormatters.get(locale).format(num); } function truncate(text, maxLength) { if (text.length <= maxLength) return text; return text.slice(0, maxLength - 3) + "..."; } function deepMerge(target, ...sources) { if (!sources.length) return target; const source = sources.shift(); if (isObject(target) && isObject(source)) { for (const key in source) { if (isObject(source[key])) { if (!target[key]) Object.assign(target, { [key]: {} }); deepMerge( target[key], source[key] ); } else { Object.assign(target, { [key]: source[key] }); } } } return deepMerge(target, ...sources); } function isObject(item) { return item !== null && typeof item === "object" && !Array.isArray(item); } function clamp(value, min, max) { return Math.min(Math.max(value, min), max); } var isBrowser = typeof window !== "undefined"; function prefersReducedMotion() { if (!isBrowser) return false; return window.matchMedia("(prefers-reduced-motion: reduce)").matches; } // src/utils/typography.ts var typographyVariants = { // Content variants body: { defaultSize: "base", defaultWeight: "normal", defaultColor: "text-primary", semanticElement: "p", description: "Standard body text for content" }, "body-large": { defaultSize: "lg", defaultWeight: "normal", defaultColor: "text-primary", semanticElement: "p", description: "Larger body text for emphasis" }, "body-small": { defaultSize: "sm", defaultWeight: "normal", defaultColor: "text-primary", semanticElement: "p", description: "Smaller body text for secondary content" }, caption: { defaultSize: "sm", defaultWeight: "normal", defaultColor: "text-secondary", semanticElement: "span", description: "Captions and secondary information" }, overline: { defaultSize: "xs", defaultWeight: "medium", defaultColor: "text-secondary", semanticElement: "span", description: "Overline text with uppercase styling" }, label: { defaultSize: "sm", defaultWeight: "medium", defaultColor: "text-primary", semanticElement: "label", description: "Form labels and UI labels" }, helper: { defaultSize: "xs", defaultWeight: "normal", defaultColor: "text-secondary", semanticElement: "span", description: "Helper text and descriptions" }, // Display variants display: { defaultSize: "6xl", defaultWeight: "bold", defaultColor: "text-primary", semanticElement: "h1", description: "Large display text for hero sections" }, headline: { defaultSize: "4xl", defaultWeight: "bold", defaultColor: "text-primary", semanticElement: "h1", description: "Headlines and page titles" }, title: { defaultSize: "2xl", defaultWeight: "semibold", defaultColor: "text-primary", semanticElement: "h2", description: "Section titles and headings" }, subtitle: { defaultSize: "lg", defaultWeight: "medium", defaultColor: "text-secondary", semanticElement: "h3", description: "Subtitles and subheadings" }, // Specialized variants code: { defaultSize: "sm", defaultWeight: "normal", defaultColor: "text-primary", semanticElement: "code", description: "Inline code and monospace text" }, kbd: { defaultSize: "xs", defaultWeight: "medium", defaultColor: "text-primary", semanticElement: "kbd", description: "Keyboard shortcuts and keys" }, quote: { defaultSize: "lg", defaultWeight: "normal", defaultColor: "text-secondary", semanticElement: "blockquote", description: "Quotes and testimonials" }, lead: { defaultSize: "xl", defaultWeight: "normal", defaultColor: "text-primary", semanticElement: "p", description: "Lead paragraphs and introductions" }, muted: { defaultSize: "base", defaultWeight: "normal", defaultColor: "text-tertiary", semanticElement: "span", description: "Muted text with reduced emphasis" } }; var sizeClasses = { xs: "text-xs", sm: "text-sm", base: "text-base", lg: "text-lg", xl: "text-xl", "2xl": "text-2xl", "3xl": "text-3xl", "4xl": "text-4xl", "5xl": "text-5xl", "6xl": "text-6xl", "7xl": "text-7xl", "8xl": "text-8xl", "9xl": "text-9xl" }; var weightClasses = { thin: "font-thin", extralight: "font-extralight", light: "font-light", normal: "font-normal", medium: "font-medium", semibold: "font-semibold", bold: "font-bold", extrabold: "font-extrabold", black: "font-black" }; var alignmentClasses = { left: "text-left", center: "text-center", right: "text-right", justify: "text-justify", start: "text-start", end: "text-end" }; var transformClasses = { none: "", uppercase: "uppercase", lowercase: "lowercase", capitalize: "capitalize" }; var decorationClasses = { none: "no-underline", underline: "underline", overline: "overline", "line-through": "line-through" }; var whitespaceClasses = { normal: "whitespace-normal", nowrap: "whitespace-nowrap", pre: "whitespace-pre", "pre-line": "whitespace-pre-line", "pre-wrap": "whitespace-pre-wrap", "break-spaces": "whitespace-break-spaces" }; var overflowClasses = { visible: "overflow-visible", hidden: "overflow-hidden", clip: "text-clip", ellipsis: "text-ellipsis" }; var leadingClasses = { none: "leading-none", tight: "leading-tight", snug: "leading-snug", normal: "leading-normal", relaxed: "leading-relaxed", loose: "leading-loose" }; var trackingClasses = { tighter: "tracking-tighter", tight: "tracking-tight", normal: "tracking-normal", wide: "tracking-wide", wider: "tracking-wider", widest: "tracking-widest" }; var lineClampClasses = { 1: "line-clamp-1", 2: "line-clamp-2", 3: "line-clamp-3", 4: "line-clamp-4", 5: "line-clamp-5", 6: "line-clamp-6" }; function generateResponsiveClasses(value, classMap) { if (typeof value === "string" || typeof value === "number") { const className = classMap[value]; return className ? [className] : []; } if (typeof value === "object" && value !== null) { const classes = []; if ("base" in value && value.base) { const className = classMap[value.base]; if (className) classes.push(className); } Object.entries(value).forEach(([breakpoint, val]) => { if (breakpoint !== "base" && val) { const className = classMap[val]; if (className) { const responsiveClass = breakpoint === "xs" ? className : `${breakpoint}:${className}`; classes.push(responsiveClass); } } }); return classes; } return []; } var typographyScales = { default: { name: "Default Scale", sizes: { xs: { fontSize: "0.75rem", lineHeight: "1rem" }, sm: { fontSize: "0.875rem", lineHeight: "1.25rem" }, base: { fontSize: "1rem", lineHeight: "1.5rem" }, lg: { fontSize: "1.125rem", lineHeight: "1.75rem" }, xl: { fontSize: "1.25rem", lineHeight: "1.75rem" }, "2xl": { fontSize: "1.5rem", lineHeight: "2rem" }, "3xl": { fontSize: "1.875rem", lineHeight: "2.25rem" }, "4xl": { fontSize: "2.25rem", lineHeight: "2.5rem" }, "5xl": { fontSize: "3rem", lineHeight: "1" }, "6xl": { fontSize: "3.75rem", lineHeight: "1" }, "7xl": { fontSize: "4.5rem", lineHeight: "1" }, "8xl": { fontSize: "6rem", lineHeight: "1" }, "9xl": { fontSize: "8rem", lineHeight: "1" } }, weights: { thin: "100", extralight: "200", light: "300", normal: "400", medium: "500", semibold: "600", bold: "700", extrabold: "800", black: "900" } }, compact: { name: "Compact Scale", sizes: { xs: { fontSize: "0.625rem", lineHeight: "0.875rem" }, sm: { fontSize: "0.75rem", lineHeight: "1rem" }, base: { fontSize: "0.875rem", lineHeight: "1.25rem" }, lg: { fontSize: "1rem", lineHeight: "1.5rem" }, xl: { fontSize: "1.125rem", lineHeight: "1.5rem" }, "2xl": { fontSize: "1.25rem", lineHeight: "1.75rem" }, "3xl": { fontSize: "1.5rem", lineHeight: "2rem" }, "4xl": { fontSize: "1.875rem", lineHeight: "2.25rem" }, "5xl": { fontSize: "2.25rem", lineHeight: "2.5rem" }, "6xl": { fontSize: "3rem", lineHeight: "1" }, "7xl": { fontSize: "3.75rem", lineHeight: "1" }, "8xl": { fontSize: "4.5rem", lineHeight: "1" }, "9xl": { fontSize: "6rem", lineHeight: "1" } }, weights: { thin: "100", extralight: "200", light: "300", normal: "400", medium: "500", semibold: "600", bold: "700", extrabold: "800", black: "900" } }, generous: { name: "Generous Scale", sizes: { xs: { fontSize: "0.875rem", lineHeight: "1.25rem" }, sm: { fontSize: "1rem", lineHeight: "1.5rem" }, base: { fontSize: "1.125rem", lineHeight: "1.75rem" }, lg: { fontSize: "1.25rem", lineHeight: "1.875rem" }, xl: { fontSize: "1.5rem", lineHeight: "2rem" }, "2xl": { fontSize: "1.875rem", lineHeight: "2.25rem" }, "3xl": { fontSize: "2.25rem", lineHeight: "2.5rem" }, "4xl": { fontSize: "3rem", lineHeight: "1.2" }, "5xl": { fontSize: "3.75rem", lineHeight: "1.1" }, "6xl": { fontSize: "4.5rem", lineHeight: "1" }, "7xl": { fontSize: "6rem", lineHeight: "1" }, "8xl": { fontSize: "8rem", lineHeight: "1" }, "9xl": { fontSize: "10rem", lineHeight: "1" } }, weights: { thin: "100", extralight: "200", light: "300", normal: "400", medium: "500", semibold: "600", bold: "700", extrabold: "800", black: "900" } } }; function getVariantConfig(variant) { const config = typographyVariants[variant]; if (!config && process.env.NODE_ENV === "development") { console.warn( `Invalid typography variant: ${variant}. Falling back to 'body' variant.` ); return typographyVariants.body; } return config || typographyVariants.body; } function getSemanticElement(variant, as) { if (as) return as; const variantConfig = typographyVariants[variant]; if (!variantConfig) return "span"; const semanticElement = variantConfig.semanticElement; return semanticElement || "span"; } function generateTypographyClasses({ variant = "body", size, weight, align, transform, decoration, whitespace, overflow, leading, tracking, lineClamp, truncate: truncate2, color, gradient, selectable }) { const variantConfig = getVariantConfig(variant); const classes = []; if (variant === "overline") { classes.push("uppercase", "tracking-wide"); } if (variant === "code") { classes.push("font-mono"); } if (variant === "kbd") { classes.push( "font-mono", "px-1.5", "py-0.5", "text-xs", "bg-surface-secondary", "border", "border-border-primary", "rounded" ); } if (variant === "quote") { classes.push("italic", "border-l-4", "border-border-primary", "pl-4"); } if (size) { classes.push(...generateResponsiveClasses(size, sizeClasses)); } else { classes.push(sizeClasses[variantConfig.defaultSize]); } if (weight) { classes.push(...generateResponsiveClasses(weight, weightClasses)); } else { classes.push(weightClasses[variantConfig.defaultWeight]); } if (align) { classes.push(...generateResponsiveClasses(align, alignmentClasses)); } if (transform && transform !== "none") { classes.push(transformClasses[transform]); } if (decoration && decoration !== "none") { classes.push(decorationClasses[decoration]); } if (whitespace && whitespace !== "normal") { classes.push(whitespaceClasses[whitespace]); } if (overflow && overflow !== "visible") { classes.push(overflowClasses[overflow]); } if (leading && leading !== "normal") { classes.push(leadingClasses[leading]); } if (tracking && tracking !== "normal") { classes.push(trackingClasses[tracking]); } if (lineClamp) { classes.push(lineClampClasses[lineClamp]); } if (truncate2) { classes.push("truncate"); } if (color && (color.startsWith("text-") || color.startsWith("bg-"))) { classes.push(color); } else { classes.push(variantConfig.defaultColor); } if (gradient) { classes.push("bg-gradient-to-r", "bg-clip-text", "text-transparent"); } if (selectable === false) { classes.push("select-none"); } else if (selectable === true) { classes.push("select-text"); } return classNames(...classes); } function generateHeadingClasses(level) { const headingMap = { 1: { defaultSize: "4xl", defaultWeight: "bold" }, 2: { defaultSize: "3xl", defaultWeight: "bold" }, 3: { defaultSize: "2xl", defaultWeight: "semibold" }, 4: { defaultSize: "xl", defaultWeight: "semibold" }, 5: { defaultSize: "lg", defaultWeight: "medium" }, 6: { defaultSize: "base", defaultWeight: "medium" } }; return headingMap[level]; } function validateTypographyProps(props) { const errors = []; if (props.variant && typeof props.variant === "string" && !(props.variant in typographyVariants)) { errors.push(`Invalid variant: ${props.variant}`); } if (props.size && typeof props.size === "string" && !(props.size in sizeClasses)) { errors.push(`Invalid size: ${props.size}`); } if (props.weight && typeof props.weight === "string" && !(props.weight in weightClasses)) { errors.push(`Invalid weight: ${props.weight}`); } return errors; } // src/types/form.ts var FORM_FIELD_SIZES = ["sm", "md", "lg"]; var FORM_FIELD_VARIANTS = ["default", "error", "success"]; // src/types/index.ts var BUTTON_VARIANTS = [ "primary", "secondary", "ghost", "danger", "success", "warning" ]; var BUTTON_SIZES = ["xs", "sm", "md", "lg", "xl"]; var INPUT_VARIANTS = ["default", "error", "success"]; var INPUT_SIZES = ["sm", "md", "lg"]; var LABEL_SIZES = ["sm", "md", "lg"]; var ICON_SIZES = ["xs", "sm", "md", "lg", "xl", "2xl"]; var BADGE_VARIANTS = [ "default", "primary", "secondary", "success", "warning", "danger" ]; var BADGE_SIZES = ["sm", "md", "lg"]; var FEATURE_CHIP_VARIANTS = [ "default", "primary", "secondary", "success", "warning", "danger" ]; var FEATURE_CHIP_SIZES = ["sm", "md", "lg"]; var AVATAR_SIZES = ["xs", "sm", "md", "lg", "xl", "2xl"]; var TEXT_VARIANTS = [ // Content variants "body", "body-large", "body-small", "caption", "overline", "label", "helper", // Display variants "display", "headline", "title", "subtitle", // Specialized variants "code", "kbd", "quote", "lead", "muted" ]; var TEXT_SIZES = [ "xs", // 12px "sm", // 14px "base", // 16px (renamed from 'md' for clarity) "lg", // 18px "xl", // 20px "2xl", // 24px "3xl", // 30px "4xl", // 36px "5xl", // 48px "6xl", // 60px "7xl", // 72px "8xl", // 96px "9xl" // 128px ]; var TEXT_WEIGHTS = [ "thin", // 100 "extralight", // 200 "light", // 300 "normal", // 400 "medium", // 500 "semibold", // 600 "bold", // 700 "extrabold", // 800 "black" // 900 ]; var TEXT_ALIGNMENTS = [ "left", "center", "right", "justify", "start", "end" ]; var TEXT_TRANSFORMS = [ "none", "uppercase", "lowercase", "capitalize" ]; var TEXT_DECORATIONS = [ "none", "underline", "overline", "line-through" ]; var TEXT_WHITESPACE = [ "normal", "nowrap", "pre", "pre-line", "pre-wrap", "break-spaces" ]; var TEXT_OVERFLOW = ["visible", "hidden", "clip", "ellipsis"]; var LINE_CLAMP_OPTIONS = [1, 2, 3, 4, 5, 6]; var TYPOGRAPHY_BREAKPOINTS = [ "xs", "sm", "md", "lg", "xl", "2xl" ]; // src/contexts/SettingsContext.tsx import { createContext, useContext, useEffect, useState as useState2 } from "react"; import { jsx } from "react/jsx-runtime"; var defaultSettings = { theme: "system", layout: "stacked", containerWidth: "boxed" }; var SettingsContext = createContext( void 0 ); var SettingsProvider = ({ children, initialSettings = {}, storageKey = "mfe-settings" }) => { const [settings, setSettings] = useState2(() => { const savedSettings = storage.get(storageKey, {}); return { ...defaultSettings, ...initialSettings, ...savedSettings }; }); useEffect(() => { theme.applyTheme(settings.theme); }, [settings.theme]); useEffect(() => { if (settings.theme !== "system") return; const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)"); const handleChange = () => { theme.applyTheme("system"); }; mediaQuery.addEventListener("change", handleChange); return () => mediaQuery.removeEventListener("change", handleChange); }, [settings.theme]); const updateSettings = (newSettings) => { setSettings((prev) => { const updated = { ...prev, ...newSettings }; storage.set(storageKey, updated); return updated; }); }; const resetSettings = () => { const reset = { ...defaultSettings, ...initialSettings }; setSettings(reset); storage.set(storageKey, reset); }; const value = { settings, updateSettings, resetSettings }; return /* @__PURE__ */ jsx(SettingsContext.Provider, { value, children }); }; var useSettings = () => { const context = useContext(SettingsContext); if (context === void 0) { throw new Error("useSettings must be used within a SettingsProvider"); } return context; }; // src/contexts/TypographyContext.tsx import { createContext as createContext2, useContext as useContext2, useMemo } from "react"; import { jsx as jsx2 } from "react/jsx-runtime"; var TypographyContext = createContext2(null); function TypographyProvider({ children, baseSize = "base", baseWeight = "normal", baseColor = "text-text-primary", scale = 1, lineHeight = 1.5 }) { const contextValue = useMemo( () => ({ baseSize, baseWeight, baseColor, scale, lineHeight }), [baseSize, baseWeight, baseColor, scale, lineHeight] ); return /* @__PURE__ */ jsx2(TypographyContext.Provider, { value: contextValue, children }); } function useTypography() { const context = useContext2(TypographyContext); if (!context) { return { baseSize: "base", baseWeight: "normal", baseColor: "text-text-primary", scale: 1, lineHeight: 1.5 }; } return context; } function useScaledTypography() { const { scale, baseSize, baseWeight, baseColor } = useTypography(); return useMemo(() => { const sizeMap = { xs: scale > 1.2 ? "sm" : "xs", sm: scale > 1.2 ? "base" : "sm", base: scale > 1.2 ? "lg" : scale < 0.8 ? "sm" : "base", lg: scale > 1.2 ? "xl" : scale < 0.8 ? "base" : "lg", xl: scale > 1.2 ? "2xl" : scale < 0.8 ? "lg" : "xl", "2xl": scale > 1.2 ? "3xl" : scale < 0.8 ? "xl" : "2xl", "3xl": scale > 1.2 ? "4xl" : scale < 0.8 ? "2xl" : "3xl", "4xl": scale > 1.2 ? "5xl" : scale < 0.8 ? "3xl" : "4xl", "5xl": scale > 1.2 ? "6xl" : scale < 0.8 ? "4xl" : "5xl", "6xl": scale > 1.2 ? "7xl" : scale < 0.8 ? "5xl" : "6xl", "7xl": scale > 1.2 ? "8xl" : scale < 0.8 ? "6xl" : "7xl", "8xl": scale > 1.2 ? "9xl" : scale < 0.8 ? "7xl" : "8xl", "9xl": scale < 0.8 ? "8xl" : "9xl" }; return { getScaledSize: (size) => sizeMap[size] || size, baseSize, baseWeight, baseColor, scale }; }, [scale, baseSize, baseWeight, baseColor]); } // src/components/atoms/Button.tsx import { forwardRef, useCallback as useCallback2 } from "react"; // src/utils/componentUtils.ts var BASE_INTERACTIVE_CLASSES = "inline-flex items-center justify-center transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2"; var createSemanticColorVariant = (colorName, type = "soft") => { const baseColor = colorName === "danger" ? "error" : colorName; const variants = { solid: `bg-${baseColor}-600 text-white hover:bg-${baseColor}-700 dark:bg-${baseColor}-500 dark:hover:bg-${baseColor}-600`, soft: `bg-${baseColor}-50 text-${baseColor}-700 border border-${baseColor}-200 dark:bg-${baseColor}-900/30 dark:text-${baseColor}-300 dark:border-${baseColor}-700/50`, outline: `border border-${baseColor}-300 text-${baseColor}-700 hover:bg-${baseColor}-50 dark:border-${baseColor}-600 dark:text-${baseColor}-300 dark:hover:bg-${baseColor}-900/20`, ghost: `text-${baseColor}-700 hover:bg-${baseColor}-50 dark:text-${baseColor}-300 dark:hover:bg-${baseColor}-900/20` }; return variants[type]; }; var createSemanticFocusRing = (colorName) => { const baseColor = colorName === "danger" ? "error" : colorName; return `focus:ring-${baseColor}-500 dark:focus:ring-${baseColor}-400`; }; var SIZE_MAPPINGS = { padding: { xs: "px-2 py-1", sm: "px-2.5 py-1.5", md: "px-3 py-2", lg: "px-4 py-2.5", xl: "px-6 py-3" }, text: { xs: "text-xs", sm: "text-sm", md: "text-sm", lg: "text-base", xl: "text-lg" }, gap: { xs: "gap-1", sm: "gap-1.5", md: "gap-2", lg: "gap-2.5", xl: "gap-3" }, icon: { xs: "w-3 h-3", sm: "w-4 h-4", md: "w-5 h-5", lg: "w-6 h-6", xl: "w-8 h-8" } }; var createSizeClasses = (size, options = {}) => { const { includePadding = true, includeText = true, includeGap = true, customPadding, customText } = options; const classes = []; if (includePadding) { const paddingMap = customPadding || SIZE_MAPPINGS.padding; classes.push(paddingMap[size] || paddingMap.md); } if (includeText) { const textMap = customText || SIZE_MAPPINGS.text; classes.push(textMap[size] || textMap.md); } if (includeGap) { classes.push( SIZE_MAPPINGS.gap[size] || SIZE_MAPPINGS.gap.md ); } return classes.join(" "); }; var createAriaAttributes = (props) => { const attributes = {}; if (props.disabled) attributes["aria-disabled"] = true; if (props.loading) attributes["aria-busy"] = true; if (props.expanded !== void 0) attributes["aria-expanded"] = props.expanded; if (props.selected !== void 0) attributes["aria-selected"] = props.selected; if (props.label) attributes["aria-label"] = props.label; if (props.describedBy) attributes["aria-describedby"] = props.describedBy; return attributes; }; var SURFACE_VARIANTS = { primary: "bg-surface-primary text-text-primary border-border-primary", secondary: "bg-surface-secondary text-text-primary border-border-primary", tertiary: "bg-surface-tertiary text-text-secondary border-border-secondary", elevated: "bg-surface-elevated text-text-primary border-border-primary shadow-sm" }; var INTERACTIVE_STATES = { hover: "hover:bg-surface-secondary", active: "active:bg-surface-tertiary", focus: "focus:ring-border-focus", disabled: "disabled:opacity-50 disabled:cursor-not-allowed" }; // src/components/atoms/LoadingSpinner.tsx import { memo } from "react"; import { jsx as jsx3, jsxs } from "react/jsx-runtime"; var LoadingSpinner = memo( ({ className, size = "md", color = "primary", text, "aria-label": ariaLabel = "Loading", ...props }) => { const sizeClasses3 = { xs: "h-3 w-3", sm: "h-4 w-4", md: "h-8 w-8", lg: "h-12 w-12", xl: "h-16 w-16" }; const colorClasses = { primary: "border-surface-secondary border-t-primary-600", secondary: "border-surface-secondary border-t-text-secondary", success: "border-surface-secondary border-t-success-600", warning: "border-surface-secondary border-t-warning-600", danger: "border-surface-secondary border-t-danger-600", white: "border-white/30 border-t-white", current: "border-current/30 border-t-current" }; const textSizeClasses = { xs: "text-xs", sm: "text-xs", md: "text-sm", lg: "text-base", xl: "text-lg" }; return /* @__PURE__ */ jsxs( "div", { className: classNames( "flex flex-col items-center justify-center", className ), ...props, children: [ /* @__PURE__ */ jsx3( "div", { className: classNames( "animate-spin rounded-full border-2", sizeClasses3[size], colorClasses[color] ), role: "status", "aria-label": ariaLabel, children: /* @__PURE__ */ jsx3("span", { className: "sr-only", children: ariaLabel }) } ), text && /* @__PURE__ */ jsx3( "p", { className: classNames( "mt-2 text-text-secondary", textSizeClasses[size] ), children: text } ) ] } ); } ); LoadingSpinner.displayName = "LoadingSpinner"; var LoadingSpinner_default = LoadingSpinner; // src/components/atoms/Button.tsx import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime"; var ButtonComponent = forwardRef( ({ children, className, variant = "primary", size = "md", disabled = false, loading = false, leftIcon, rightIcon, fullWidth = false, as: Component = "button", ...props }, ref) => { const bas