import { CSSProperties } from "react"; /** * Design tokens - centralized values for consistency. */ const tokens = { // Font families fontSans: "'DM Sans', system-ui, sans-serif", fontSerif: "'Instrument Serif', Georgia, serif", fontMono: "'DM Mono', monospace", // Colors white: "#fff", textPrimary: "#fff", textSecondary: "rgba(255, 255, 255, 0.7)", textMuted: "rgba(255, 255, 255, 0.5)", textDim: "rgba(255, 255, 255, 0.4)", textDisabled: "rgba(255, 255, 255, 0.3)", // Accent colors accent: "#a78bfa", accentIndigo: "rgba(99, 102, 241, 0.9)", accentIndigoMuted: "rgba(129, 140, 248, 0.9)", // Status colors success: "rgba(34, 197, 94, 0.9)", successBg: "rgba(34, 197, 94, 0.2)", successBorder: "rgba(34, 197, 94, 0.3)", error: "#f87171", errorBg: "rgba(239, 68, 68, 0.15)", errorBorder: "rgba(239, 68, 68, 0.3)", // Surfaces surfaceBg: "rgba(255, 255, 255, 0.03)", surfaceBorder: "rgba(255, 255, 255, 0.08)", surfaceHover: "rgba(255, 255, 255, 0.05)", inputBg: "rgba(255, 255, 255, 0.05)", inputBorder: "rgba(255, 255, 255, 0.1)", // Gradients pageGradient: "linear-gradient(135deg, #0f0f23 0%, #1a1a3e 50%, #2d1b4e 100%)", primaryGradient: "linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)", // Shadows cardShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.5)", buttonShadow: "0 4px 14px rgba(99, 102, 241, 0.4)", // Border radius radiusSm: "6px", radiusMd: "8px", radiusLg: "12px", radiusXl: "16px", radius2xl: "20px", radius3xl: "24px", } as const; type StyleRecord = Record; // ============================================================================= // Layout Styles // ============================================================================= export const layoutStyles: StyleRecord = { /** Full-page main container with gradient background */ main: { minHeight: "100vh", background: tokens.pageGradient, display: "flex", flexDirection: "column", }, /** Centered loading indicator */ loader: { flex: 1, display: "flex", alignItems: "center", justifyContent: "center", fontFamily: tokens.fontSans, color: tokens.textMuted, fontSize: "1.125rem", }, /** Content area - centered (for cards/forms) */ contentCentered: { flex: 1, display: "flex", alignItems: "center", justifyContent: "center", padding: "2rem", }, /** Content area - scrollable (for tables/lists) */ contentScrollable: { flex: 1, padding: "2rem", overflowY: "auto", }, /** Max-width container for page content */ pageContainer: { maxWidth: "1200px", margin: "0 auto", width: "100%", }, }; // ============================================================================= // Header Styles (for nav/app header - used by Header component) // ============================================================================= export const headerStyles: StyleRecord = { header: { padding: "1.5rem 2rem", borderBottom: `1px solid ${tokens.surfaceBorder}`, display: "flex", justifyContent: "space-between", alignItems: "center", }, nav: { display: "flex", alignItems: "center", gap: "0.75rem", }, navLink: { fontFamily: tokens.fontSans, color: tokens.textMuted, fontSize: "0.875rem", textDecoration: "none", transition: "color 0.2s", }, navDivider: { color: "rgba(255, 255, 255, 0.2)", fontSize: "0.75rem", }, navCurrent: { fontFamily: tokens.fontSans, color: tokens.accent, fontSize: "0.875rem", fontWeight: 600, }, userInfo: { display: "flex", alignItems: "center", gap: "1rem", }, userEmail: { fontFamily: tokens.fontSans, color: "rgba(255, 255, 255, 0.6)", fontSize: "0.875rem", }, logoutBtn: { fontFamily: tokens.fontSans, padding: "0.5rem 1rem", fontSize: "0.875rem", fontWeight: 500, background: tokens.surfaceHover, color: tokens.textSecondary, border: `1px solid ${tokens.inputBorder}`, borderRadius: tokens.radiusMd, cursor: "pointer", transition: "all 0.2s", }, }; // ============================================================================= // Typography Styles // ============================================================================= export const typographyStyles: StyleRecord = { /** Large page title - serif font */ pageTitle: { fontFamily: tokens.fontSerif, fontSize: "2rem", fontWeight: 400, color: tokens.white, margin: 0, letterSpacing: "-0.02em", }, /** Page subtitle text */ pageSubtitle: { fontFamily: tokens.fontSans, color: tokens.textMuted, marginTop: "0.5rem", fontSize: "0.95rem", }, /** Section title - uppercase, smaller */ sectionTitle: { fontFamily: tokens.fontSans, fontSize: "0.875rem", fontWeight: 600, color: "rgba(255, 255, 255, 0.8)", margin: 0, textTransform: "uppercase", letterSpacing: "0.05em", }, /** Section title - muted variant */ sectionTitleMuted: { fontFamily: tokens.fontSans, fontSize: "1.1rem", fontWeight: 500, color: tokens.textMuted, marginBottom: "1rem", }, /** Section hint text */ sectionHint: { fontFamily: tokens.fontSans, fontSize: "0.8rem", color: tokens.textDim, margin: 0, }, }; // ============================================================================= // Card Styles // ============================================================================= export const cardStyles: StyleRecord = { /** Standard card container */ card: { background: tokens.surfaceBg, backdropFilter: "blur(10px)", border: `1px solid ${tokens.surfaceBorder}`, borderRadius: tokens.radius3xl, padding: "2.5rem", boxShadow: tokens.cardShadow, }, /** Card header section */ cardHeader: { marginBottom: "2rem", }, /** Card title - serif, large */ cardTitle: { fontFamily: tokens.fontSerif, fontSize: "2rem", fontWeight: 400, color: tokens.white, margin: 0, letterSpacing: "-0.02em", }, /** Card subtitle */ cardSubtitle: { fontFamily: tokens.fontSans, color: tokens.textMuted, marginTop: "0.5rem", fontSize: "0.95rem", }, /** Smaller table/section card */ tableCard: { background: tokens.surfaceBg, backdropFilter: "blur(10px)", border: `1px solid ${tokens.surfaceBorder}`, borderRadius: tokens.radius2xl, padding: "1.5rem", boxShadow: tokens.cardShadow, }, }; // ============================================================================= // Table Styles // ============================================================================= export const tableStyles: StyleRecord = { tableHeader: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "1rem", flexWrap: "wrap", gap: "1rem", }, tableTitle: { fontFamily: tokens.fontSerif, fontSize: "1.5rem", fontWeight: 400, color: tokens.white, margin: 0, }, totalCount: { fontFamily: tokens.fontSans, fontSize: "0.875rem", color: tokens.textDim, }, tableWrapper: { overflowX: "auto", }, table: { width: "100%", borderCollapse: "collapse", fontFamily: tokens.fontSans, }, th: { textAlign: "left", padding: "0.75rem 1rem", fontSize: "0.75rem", fontWeight: 600, color: tokens.textDim, textTransform: "uppercase", letterSpacing: "0.05em", borderBottom: `1px solid ${tokens.surfaceBorder}`, }, tr: { borderBottom: "1px solid rgba(255, 255, 255, 0.04)", }, td: { padding: "0.875rem 1rem", fontSize: "0.875rem", color: tokens.textSecondary, }, tdMono: { padding: "0.875rem 1rem", fontSize: "0.875rem", color: tokens.white, fontFamily: tokens.fontMono, }, tdNum: { padding: "0.875rem 1rem", fontSize: "0.875rem", color: "rgba(255, 255, 255, 0.9)", fontFamily: tokens.fontMono, }, tdDate: { padding: "0.875rem 1rem", fontSize: "0.75rem", color: tokens.textDim, }, emptyRow: { padding: "2rem 1rem", textAlign: "center", color: tokens.textDisabled, fontSize: "0.875rem", }, errorRow: { padding: "2rem 1rem", textAlign: "center", color: tokens.error, fontSize: "0.875rem", }, }; // ============================================================================= // Pagination Styles // ============================================================================= export const paginationStyles: StyleRecord = { pagination: { display: "flex", justifyContent: "center", alignItems: "center", gap: "1rem", marginTop: "1rem", paddingTop: "1rem", borderTop: "1px solid rgba(255, 255, 255, 0.06)", }, pageBtn: { fontFamily: tokens.fontSans, padding: "0.5rem 1rem", fontSize: "1rem", background: tokens.surfaceHover, color: tokens.textSecondary, border: `1px solid ${tokens.inputBorder}`, borderRadius: tokens.radiusMd, cursor: "pointer", transition: "all 0.2s", }, pageInfo: { fontFamily: tokens.fontSans, fontSize: "0.875rem", color: tokens.textMuted, }, }; // ============================================================================= // Form/Input Styles // ============================================================================= export const formStyles: StyleRecord = { form: { display: "flex", flexDirection: "column", gap: "1.25rem", }, field: { display: "flex", flexDirection: "column", gap: "0.5rem", }, label: { fontFamily: tokens.fontSans, color: tokens.textSecondary, fontSize: "0.875rem", fontWeight: 500, }, input: { fontFamily: tokens.fontSans, padding: "0.875rem 1rem", fontSize: "1rem", background: tokens.inputBg, border: `1px solid ${tokens.inputBorder}`, borderRadius: tokens.radiusLg, color: tokens.white, outline: "none", transition: "border-color 0.2s, box-shadow 0.2s", }, inputError: { border: `1px solid ${tokens.errorBorder}`, boxShadow: `0 0 0 2px ${tokens.errorBg}`, }, inputReadOnly: { background: "rgba(255, 255, 255, 0.02)", color: tokens.textMuted, cursor: "not-allowed", }, textarea: { fontFamily: tokens.fontSans, width: "100%", padding: "0.75rem", background: tokens.inputBg, border: `1px solid ${tokens.inputBorder}`, borderRadius: tokens.radiusMd, color: tokens.white, fontSize: "0.875rem", minHeight: "80px", resize: "vertical" as const, }, select: { fontFamily: tokens.fontSans, fontSize: "0.9rem", padding: "0.75rem", background: tokens.inputBg, border: `1px solid ${tokens.inputBorder}`, borderRadius: tokens.radiusMd, color: tokens.white, cursor: "pointer", }, hint: { fontFamily: tokens.fontSans, fontSize: "0.75rem", color: tokens.textDim, fontStyle: "italic", }, errorText: { fontFamily: tokens.fontSans, fontSize: "0.75rem", color: "#fca5a5", }, error: { fontFamily: tokens.fontSans, fontSize: "0.75rem", color: "#fca5a5", marginTop: "0.25rem", }, section: { marginBottom: "2rem", }, sectionTitle: { fontFamily: tokens.fontSans, fontSize: "1rem", fontWeight: 600, color: tokens.textSecondary, marginBottom: "1.25rem", marginTop: 0, }, actions: { display: "flex", justifyContent: "flex-end", marginTop: "2rem", paddingTop: "1.5rem", borderTop: `1px solid ${tokens.surfaceBorder}`, }, charCount: { fontFamily: tokens.fontSans, fontSize: "0.75rem", color: tokens.textDim, textAlign: "right" as const, marginTop: "0.25rem", }, charCountWarning: { color: tokens.error, }, }; // ============================================================================= // Button Styles // ============================================================================= export const buttonStyles: StyleRecord = { /** Primary action button with gradient */ primaryButton: { fontFamily: tokens.fontSans, padding: "1rem", fontSize: "1rem", fontWeight: 600, background: tokens.primaryGradient, color: tokens.white, border: "none", borderRadius: tokens.radiusLg, cursor: "pointer", transition: "transform 0.2s, box-shadow 0.2s", boxShadow: tokens.buttonShadow, }, /** Secondary/cancel button */ secondaryButton: { fontFamily: tokens.fontSans, fontSize: "0.85rem", padding: "0.6rem 1rem", background: tokens.surfaceHover, color: tokens.textSecondary, border: `1px solid ${tokens.inputBorder}`, borderRadius: tokens.radiusMd, cursor: "pointer", transition: "all 0.2s", }, /** Accent button (indigo tinted) */ accentButton: { fontFamily: tokens.fontSans, fontSize: "0.9rem", fontWeight: 500, padding: "0.75rem 1.5rem", background: "rgba(99, 102, 241, 0.3)", color: tokens.white, border: "1px solid rgba(99, 102, 241, 0.5)", borderRadius: tokens.radiusMd, cursor: "pointer", }, /** Danger/destructive button */ dangerButton: { fontFamily: tokens.fontSans, fontSize: "0.75rem", padding: "0.4rem 0.75rem", background: tokens.errorBg, color: "rgba(239, 68, 68, 0.9)", border: `1px solid ${tokens.errorBorder}`, borderRadius: tokens.radiusSm, cursor: "pointer", }, /** Disabled button modifier */ buttonDisabled: { opacity: 0.5, cursor: "not-allowed", boxShadow: "none", }, /** Primary button alias (for backwards compatibility) */ primary: { fontFamily: tokens.fontSans, padding: "1rem", fontSize: "1rem", fontWeight: 600, background: tokens.primaryGradient, color: tokens.white, border: "none", borderRadius: tokens.radiusLg, cursor: "pointer", transition: "transform 0.2s, box-shadow 0.2s", boxShadow: tokens.buttonShadow, }, /** Disabled button alias (for backwards compatibility) */ disabled: { opacity: 0.5, cursor: "not-allowed", boxShadow: "none", }, }; // ============================================================================= // Badge/Status Styles // ============================================================================= export const badgeStyles: StyleRecord = { /** Base badge style */ badge: { fontFamily: tokens.fontSans, fontSize: "0.7rem", fontWeight: 500, padding: "0.25rem 0.5rem", borderRadius: "4px", textTransform: "uppercase", display: "inline-block", }, /** Ready/primary status */ badgeReady: { background: "rgba(99, 102, 241, 0.2)", color: tokens.accentIndigoMuted, }, /** Success/spent status */ badgeSuccess: { background: tokens.successBg, color: tokens.success, }, /** Error/revoked status */ badgeError: { background: "rgba(239, 68, 68, 0.2)", color: "rgba(239, 68, 68, 0.9)", }, /** Booked/active status */ badgeBooked: { background: "rgba(99, 102, 241, 0.15)", color: tokens.accentIndigoMuted, }, }; // ============================================================================= // Banner/Alert Styles // ============================================================================= export const bannerStyles: StyleRecord = { errorBanner: { fontFamily: tokens.fontSans, fontSize: "0.875rem", padding: "1rem", background: tokens.errorBg, border: `1px solid ${tokens.errorBorder}`, borderRadius: tokens.radiusMd, color: tokens.error, marginBottom: "1rem", }, successBanner: { fontFamily: tokens.fontSans, fontSize: "0.875rem", padding: "1rem", background: "rgba(34, 197, 94, 0.15)", border: `1px solid ${tokens.successBorder}`, borderRadius: tokens.radiusMd, color: "#4ade80", marginBottom: "1rem", }, /** Success banner alias (for backwards compatibility) */ success: { fontFamily: tokens.fontSans, fontSize: "0.875rem", padding: "1rem", background: "rgba(34, 197, 94, 0.15)", border: `1px solid ${tokens.successBorder}`, borderRadius: tokens.radiusMd, color: "#4ade80", marginBottom: "1rem", }, }; // ============================================================================= // Modal Styles // ============================================================================= export const modalStyles: StyleRecord = { modalOverlay: { position: "fixed", top: 0, left: 0, right: 0, bottom: 0, background: "rgba(0, 0, 0, 0.7)", display: "flex", alignItems: "center", justifyContent: "center", zIndex: 1000, }, modal: { background: "#1a1a3e", border: `1px solid ${tokens.inputBorder}`, borderRadius: tokens.radiusXl, padding: "2rem", width: "90%", maxWidth: "400px", boxShadow: tokens.cardShadow, }, modalTitle: { fontFamily: tokens.fontSerif, fontSize: "1.5rem", fontWeight: 400, color: tokens.white, margin: "0 0 1.5rem 0", }, modalError: { fontFamily: tokens.fontSans, fontSize: "0.85rem", padding: "0.75rem", background: tokens.errorBg, border: `1px solid ${tokens.errorBorder}`, borderRadius: tokens.radiusMd, color: tokens.error, marginBottom: "1rem", }, modalActions: { display: "flex", justifyContent: "space-between", alignItems: "center", gap: "1rem", }, modalActionsRight: { display: "flex", gap: "0.75rem", }, }; // ============================================================================= // Toast Styles // ============================================================================= export const toastStyles: StyleRecord = { toast: { position: "fixed", top: "1.5rem", right: "1.5rem", padding: "1rem 1.5rem", borderRadius: tokens.radiusLg, fontFamily: tokens.fontSans, fontSize: "0.875rem", fontWeight: 500, zIndex: 1000, boxShadow: "0 10px 25px rgba(0, 0, 0, 0.3)", }, toastSuccess: { background: "rgba(34, 197, 94, 0.9)", color: tokens.white, }, toastError: { background: "rgba(239, 68, 68, 0.9)", color: tokens.white, }, }; // ============================================================================= // Trade Card Styles (shared between trades and admin/trades pages) // ============================================================================= export const tradeCardStyles: StyleRecord = { /** Container for list of trade cards */ tradeList: { display: "flex", flexDirection: "column", gap: "0.75rem", }, /** Individual trade card */ tradeCard: { background: tokens.surfaceBg, border: `1px solid ${tokens.surfaceBorder}`, borderRadius: tokens.radiusLg, padding: "1.25rem", transition: "all 0.2s", }, /** Trade card header - flex row for info + actions */ tradeHeader: { display: "flex", justifyContent: "space-between", alignItems: "flex-start", gap: "1rem", }, /** Left side info container */ tradeInfo: { display: "flex", flexDirection: "column", gap: "0.25rem", }, /** Trade date/time display */ tradeTime: { fontFamily: tokens.fontSans, fontSize: "1rem", fontWeight: 500, color: tokens.white, marginBottom: "0.5rem", }, /** Trade amount details row */ tradeDetails: { display: "flex", alignItems: "center", gap: "0.5rem", flexWrap: "wrap", }, /** BUY/SELL direction badge (base - color applied inline) */ directionBadge: { fontFamily: tokens.fontSans, fontSize: "0.7rem", fontWeight: 600, padding: "0.2rem 0.5rem", borderRadius: "4px", textTransform: "uppercase", }, /** EUR amount display */ amount: { fontFamily: tokens.fontMono, fontSize: "0.95rem", color: tokens.white, fontWeight: 500, }, /** Arrow between EUR and sats */ arrow: { color: tokens.textDisabled, fontSize: "0.8rem", }, /** Sats amount in Bitcoin orange */ satsAmount: { fontFamily: tokens.fontMono, fontSize: "0.9rem", color: "#f7931a", // Bitcoin orange }, /** Rate info row */ rateRow: { display: "flex", alignItems: "center", gap: "0.5rem", marginTop: "0.25rem", flexWrap: "wrap", }, /** Rate label text */ rateLabel: { fontFamily: tokens.fontSans, fontSize: "0.75rem", color: tokens.textDim, }, /** Rate value in monospace */ rateValue: { fontFamily: tokens.fontMono, fontSize: "0.8rem", color: tokens.textSecondary, }, /** Button group container */ buttonGroup: { display: "flex", gap: "0.5rem", }, /** Empty state for no trades */ emptyState: { fontFamily: tokens.fontSans, color: tokens.textDim, textAlign: "center" as const, padding: "3rem", }, }; // ============================================================================= // Misc Utility Styles // ============================================================================= export const utilityStyles: StyleRecord = { /** Horizontal divider */ divider: { height: "1px", background: tokens.surfaceBorder, margin: "0.75rem 0", }, /** Empty state container */ emptyState: { fontFamily: tokens.fontSans, color: tokens.textDim, textAlign: "center" as const, padding: "1rem 0", }, /** Read-only badge for form labels */ readOnlyBadge: { fontSize: "0.7rem", fontWeight: 500, color: tokens.textDim, background: tokens.surfaceBorder, padding: "0.15rem 0.5rem", borderRadius: "4px", textTransform: "uppercase", letterSpacing: "0.05em", }, /** Flex row with gap */ buttonRow: { display: "flex", gap: "0.75rem", }, /** Filter group container */ filterGroup: { display: "flex", alignItems: "center", gap: "1rem", }, }; // ============================================================================= // Combined sharedStyles (backwards compatible export) // ============================================================================= /** * @deprecated Use individual style exports (layoutStyles, cardStyles, etc.) for better tree-shaking. * This combined export is maintained for backwards compatibility during migration. */ export const sharedStyles: StyleRecord = { // Layout main: layoutStyles.main, loader: layoutStyles.loader, content: layoutStyles.contentCentered, // Header header: headerStyles.header, nav: headerStyles.nav, navLink: headerStyles.navLink, navDivider: headerStyles.navDivider, navCurrent: headerStyles.navCurrent, userInfo: headerStyles.userInfo, userEmail: headerStyles.userEmail, logoutBtn: headerStyles.logoutBtn, // Common errorBanner: bannerStyles.errorBanner, cancelButton: buttonStyles.secondaryButton, emptyState: utilityStyles.emptyState, };