Initial commit

This commit is contained in:
vitalijmulika
2025-12-27 14:40:16 +02:00
commit e2f01d7977
307 changed files with 62614 additions and 0 deletions

View File

@@ -0,0 +1,136 @@
"use client";
import React, { createContext, useContext } from "react";
import type { ThemeConfig, ThemeProviderProps } from "./config/types";
import { borderRadiusMap, borderRadiusCappedMap, contentWidthMap, expandedContentWidthMap, baseVwMap, textSizingMap, backgroundComponents, headingFontWeightMap } from "./config/constants";
import { cardStyleMap, getGradientBorderedPseudo } from "./styles/cardStyles";
import { primaryButtonStyleMap } from "./styles/primaryButtonStyles";
import { secondaryButtonStyleMap } from "./styles/secondaryButtonStyles";
import { detectLightBackground } from "./utils/detectLightBackground";
const ThemeContext = createContext<ThemeConfig | undefined>(undefined);
export const useTheme = () => {
const context = useContext(ThemeContext);
if (!context) {
throw new Error(
"useTheme must be used within a ThemeProvider. Wrap your sections in <ThemeProvider> at the app/page level."
);
}
return context;
};
export const ThemeProvider = ({
children,
defaultButtonVariant,
defaultTextAnimation,
borderRadius,
contentWidth,
sizing,
background,
cardStyle,
primaryButtonStyle,
secondaryButtonStyle,
headingFontWeight,
}: ThemeProviderProps) => {
const themeConfig: ThemeConfig = {
defaultButtonVariant,
defaultTextAnimation,
borderRadius,
contentWidth,
sizing,
background,
cardStyle,
primaryButtonStyle,
secondaryButtonStyle,
headingFontWeight,
};
const borderRadiusValue = borderRadiusMap[borderRadius];
const borderRadiusCappedValue = borderRadiusCappedMap[borderRadius];
const contentWidthValues = contentWidthMap[contentWidth];
const expandedContentWidthValues = expandedContentWidthMap[contentWidth];
const baseVwValues = baseVwMap[sizing];
const textSizingValues = textSizingMap[sizing];
const BackgroundComponent = backgroundComponents[background];
const headingFontWeightValue = headingFontWeightMap[headingFontWeight];
const cardStyles = cardStyleMap[cardStyle];
const primaryButtonStyles = primaryButtonStyleMap[primaryButtonStyle];
const secondaryButtonStyles = secondaryButtonStyleMap[secondaryButtonStyle];
const gradientBorderedPseudo = getGradientBorderedPseudo(cardStyle);
const styleString = `
:root {
--theme-border-radius: ${borderRadiusValue};
--theme-border-radius-capped: ${borderRadiusCappedValue};
--width-content-width: ${contentWidthValues.desktop};
--width-content-width-expanded: ${expandedContentWidthValues.desktop};
--vw: ${baseVwValues.desktop};
--heading-font-weight: ${headingFontWeightValue};
--text-2xs: ${textSizingValues.desktop.text2xs};
--text-xs: ${textSizingValues.desktop.textXs};
--text-sm: ${textSizingValues.desktop.textSm};
--text-base: ${textSizingValues.desktop.textBase};
--text-lg: ${textSizingValues.desktop.textLg};
--text-xl: ${textSizingValues.desktop.textXl};
--text-2xl: ${textSizingValues.desktop.text2xl};
--text-3xl: ${textSizingValues.desktop.text3xl};
--text-4xl: ${textSizingValues.desktop.text4xl};
--text-5xl: ${textSizingValues.desktop.text5xl};
--text-6xl: ${textSizingValues.desktop.text6xl};
--text-7xl: ${textSizingValues.desktop.text7xl};
--text-8xl: ${textSizingValues.desktop.text8xl};
--text-9xl: ${textSizingValues.desktop.text9xl};
}
@media (max-width: 768px) {
:root {
--width-content-width: ${contentWidthValues.mobile};
--width-content-width-expanded: ${expandedContentWidthValues.mobile};
--vw: ${baseVwValues.mobile};
--text-2xs: ${textSizingValues.mobile.text2xs};
--text-xs: ${textSizingValues.mobile.textXs};
--text-sm: ${textSizingValues.mobile.textSm};
--text-base: ${textSizingValues.mobile.textBase};
--text-lg: ${textSizingValues.mobile.textLg};
--text-xl: ${textSizingValues.mobile.textXl};
--text-2xl: ${textSizingValues.mobile.text2xl};
--text-3xl: ${textSizingValues.mobile.text3xl};
--text-4xl: ${textSizingValues.mobile.text4xl};
--text-5xl: ${textSizingValues.mobile.text5xl};
--text-6xl: ${textSizingValues.mobile.text6xl};
--text-7xl: ${textSizingValues.mobile.text7xl};
--text-8xl: ${textSizingValues.mobile.text8xl};
--text-9xl: ${textSizingValues.mobile.text9xl};
}
}
.card {
${cardStyles}
}
${gradientBorderedPseudo}
.primary-button {
${primaryButtonStyles}
}
.secondary-button {
${secondaryButtonStyles}
}
`;
// Determine if background is light or dark for AnimatedAuroraBackground
const isLightBackground = React.useMemo(() => detectLightBackground(), []);
return (
<ThemeContext.Provider value={themeConfig}>
<style>{styleString}</style>
{BackgroundComponent && (
background === 'animatedAurora' ? (
<BackgroundComponent invertColors={isLightBackground} />
) : (
<BackgroundComponent />
)
)}
{children}
</ThemeContext.Provider>
);
};

View File

@@ -0,0 +1,573 @@
import PlainBackground from "@/components/background/PlainBackground";
import GridBackround from "@/components/background/GridBackround";
import DotGridBackground from "@/components/background/DotGridBackground";
import CircleGradientBackground from "@/components/background/CircleGradientBackground";
import AuroraBackground from "@/components/background/AuroraBackground";
import FloatingGradientBackground from "@/components/background/floatingGradientBackground/FloatingGradientBackground";
import AnimatedGridBackground from "@/components/background/AnimatedGridBackground";
import AnimatedAuroraBackground from "@/components/background/AnimatedAuroraBackground";
import FluidBackground from "@/components/background/FluidBackground";
import RadialGradientBackground from "@/components/background/RadialGradientBackground";
import NoiseBackground from "@/components/background/NoiseBackground";
import NoiseGradientBackground from "@/components/background/NoiseGradientBackground";
import NoiseDiagonalGradientBackground from "@/components/background/NoiseDiagonalGradientBackground";
import type { BorderRadiusPreset, ContentWidthPreset, SizingPreset, BackgroundType, HeadingFontWeight } from "./types";
/**
* Shared component layout and styling type definitions
*/
export type TextboxLayout = "default" | "split" | "split-actions" | "split-description" | "inline-image";
export type InvertedBackground = "noInvert" | "invertDefault" | "invertCard";
export const borderRadiusMap: Record<BorderRadiusPreset, string> = {
sharp: "0",
rounded: "var(--radius)",
soft: "var(--radius-lg)",
pill: "var(--radius-full)",
};
export const borderRadiusCappedMap: Record<BorderRadiusPreset, string> = {
sharp: "0",
rounded: "var(--radius)",
soft: "var(--radius-lg)",
pill: "var(--radius-xl)", // Capped at xl instead of full
};
export const headingFontWeightMap: Record<HeadingFontWeight, string> = {
light: "300",
normal: "400",
medium: "500",
semibold: "600",
bold: "700",
extrabold: "800",
};
export const contentWidthMap: Record<ContentWidthPreset, { desktop: string; mobile: string }> = {
small: {
desktop: "clamp(40rem, 70vw, 100rem)",
mobile: "80vw",
},
smallMedium: {
desktop: "clamp(40rem, 72.5vw, 100rem)",
mobile: "80vw",
},
compact: {
desktop: "clamp(40rem, 75vw, 100rem)",
mobile: "80vw",
},
mediumSmall: {
desktop: "clamp(40rem, 77.5vw, 100rem)",
mobile: "80vw",
},
medium: {
desktop: "clamp(40rem, 80vw, 100rem)",
mobile: "80vw",
},
mediumLarge: {
desktop: "clamp(40rem, 82.5vw, 100rem)",
mobile: "85vw",
},
};
function calculateExpandedWidth(width: string): string {
const clampMatch = width.match(/clamp\(([\d.]+)rem,\s*([\d.]+)vw,\s*([\d.]+)rem\)/);
if (clampMatch) {
const minRem = clampMatch[1];
const vwValue = parseFloat(clampMatch[2]);
const maxRem = clampMatch[3];
const remainingVw = 100 - vwValue;
const expandedVw = vwValue + (remainingVw / 2);
const expandedMin = `calc(${minRem}rem - (${minRem}rem - 100vw) / 2)`;
const expandedMax = `calc(${maxRem}rem + (100vw - ${maxRem}rem) / 2)`;
return `clamp(${expandedMin}, ${expandedVw}vw, ${expandedMax})`;
}
const vwMatch = width.match(/([\d.]+)vw/);
if (vwMatch) {
const vwValue = parseFloat(vwMatch[1]);
const remainingVw = 100 - vwValue;
const expandedVw = vwValue + (remainingVw / 2);
return `${expandedVw}vw`;
}
return width;
}
export const expandedContentWidthMap: Record<ContentWidthPreset, { desktop: string; mobile: string }> = {
small: {
desktop: calculateExpandedWidth(contentWidthMap.small.desktop),
mobile: calculateExpandedWidth(contentWidthMap.small.mobile),
},
smallMedium: {
desktop: calculateExpandedWidth(contentWidthMap.smallMedium.desktop),
mobile: calculateExpandedWidth(contentWidthMap.smallMedium.mobile),
},
compact: {
desktop: calculateExpandedWidth(contentWidthMap.compact.desktop),
mobile: calculateExpandedWidth(contentWidthMap.compact.mobile),
},
mediumSmall: {
desktop: calculateExpandedWidth(contentWidthMap.mediumSmall.desktop),
mobile: calculateExpandedWidth(contentWidthMap.mediumSmall.mobile),
},
medium: {
desktop: calculateExpandedWidth(contentWidthMap.medium.desktop),
mobile: calculateExpandedWidth(contentWidthMap.medium.mobile),
},
mediumLarge: {
desktop: calculateExpandedWidth(contentWidthMap.mediumLarge.desktop),
mobile: calculateExpandedWidth(contentWidthMap.mediumLarge.mobile),
},
};
export const baseVwMap: Record<SizingPreset, { desktop: string; mobile: string }> = {
medium: {
desktop: "clamp(0.5rem, 0.8vw, 1rem)",
mobile: "3vw",
},
mediumLarge: {
desktop: "clamp(0.5rem, 0.84vw, 1rem)",
mobile: "3.15vw",
},
largeSmall: {
desktop: "clamp(0.5rem, 0.88vw, 1rem)",
mobile: "3.30vw",
},
large: {
desktop: "clamp(0.5rem, 0.92vw, 1rem)",
mobile: "3.45vw",
},
mediumSizeLargeTitles: {
desktop: "clamp(0.5rem, 0.8vw, 1rem)",
mobile: "3vw",
},
largeSizeMediumTitles: {
desktop: "clamp(0.5rem, 0.92vw, 1rem)",
mobile: "3.45vw",
},
mediumLargeSizeLargeTitles: {
desktop: "clamp(0.5rem, 0.84vw, 1rem)",
mobile: "3.15vw",
},
largeSmallSizeLargeTitles: {
desktop: "clamp(0.5rem, 0.88vw, 1rem)",
mobile: "3.30vw",
},
mediumLargeSizeMediumTitles: {
desktop: "clamp(0.5rem, 0.84vw, 1rem)",
mobile: "3.15vw",
},
largeSmallSizeMediumTitles: {
desktop: "clamp(0.5rem, 0.88vw, 1rem)",
mobile: "3.30vw",
},
};
interface TextSizingValues {
text2xs: string;
textXs: string;
textSm: string;
textBase: string;
textLg: string;
textXl: string;
text2xl: string;
text3xl: string;
text4xl: string;
text5xl: string;
text6xl: string;
text7xl: string;
text8xl: string;
text9xl: string;
}
export const textSizingMap: Record<SizingPreset, { desktop: TextSizingValues; mobile: TextSizingValues }> = {
medium: {
desktop: {
text2xs: "clamp(0.465rem, 0.62vw, 0.62rem)",
textXs: "clamp(0.54rem, 0.72vw, 0.72rem)",
textSm: "clamp(0.615rem, 0.82vw, 0.82rem)",
textBase: "clamp(0.69rem, 0.92vw, 0.92rem)",
textLg: "clamp(0.75rem, 1vw, 1rem)",
textXl: "clamp(0.825rem, 1.1vw, 1.1rem)",
text2xl: "clamp(0.975rem, 1.3vw, 1.3rem)",
text3xl: "clamp(1.2rem, 1.6vw, 1.6rem)",
text4xl: "clamp(1.5rem, 2vw, 2rem)",
text5xl: "clamp(2.025rem, 2.75vw, 2.75rem)",
text6xl: "clamp(2.475rem, 3.3vw, 3.3rem)",
text7xl: "clamp(3rem, 4vw, 4rem)",
text8xl: "clamp(3.5rem, 4.5vw, 4.5rem)",
text9xl: "clamp(5.25rem, 7vw, 7rem)",
},
mobile: {
text2xs: "2.5vw",
textXs: "2.75vw",
textSm: "3vw",
textBase: "3.25vw",
textLg: "3.5vw",
textXl: "4.25vw",
text2xl: "5vw",
text3xl: "6vw",
text4xl: "7vw",
text5xl: "7.5vw",
text6xl: "8.5vw",
text7xl: "10vw",
text8xl: "12vw",
text9xl: "14vw",
},
},
mediumLarge: {
desktop: {
text2xs: "clamp(0.465rem, 0.651vw, 0.62rem)",
textXs: "clamp(0.54rem, 0.756vw, 0.72rem)",
textSm: "clamp(0.615rem, 0.861vw, 0.82rem)",
textBase: "clamp(0.69rem, 0.966vw, 0.92rem)",
textLg: "clamp(0.75rem, 1.05vw, 1rem)",
textXl: "clamp(0.825rem, 1.155vw, 1.1rem)",
text2xl: "clamp(0.975rem, 1.365vw, 1.3rem)",
text3xl: "clamp(1.2rem, 1.68vw, 1.6rem)",
text4xl: "clamp(1.5rem, 2.1vw, 2rem)",
text5xl: "clamp(2.025rem, 2.8875vw, 2.75rem)",
text6xl: "clamp(2.475rem, 3.465vw, 3.3rem)",
text7xl: "clamp(3rem, 4.2vw, 4rem)",
text8xl: "clamp(3.5rem, 4.725vw, 4.5rem)",
text9xl: "clamp(5.25rem, 7.35vw, 7rem)",
},
mobile: {
text2xs: "2.625vw",
textXs: "2.8875vw",
textSm: "3.15vw",
textBase: "3.4125vw",
textLg: "3.675vw",
textXl: "4.4625vw",
text2xl: "5.25vw",
text3xl: "6.3vw",
text4xl: "7.35vw",
text5xl: "7.875vw",
text6xl: "8.925vw",
text7xl: "10.5vw",
text8xl: "12.6vw",
text9xl: "14.7vw",
},
},
largeSmall: {
desktop: {
text2xs: "clamp(0.465rem, 0.682vw, 0.62rem)",
textXs: "clamp(0.54rem, 0.792vw, 0.72rem)",
textSm: "clamp(0.615rem, 0.902vw, 0.82rem)",
textBase: "clamp(0.69rem, 1.012vw, 0.92rem)",
textLg: "clamp(0.75rem, 1.1vw, 1rem)",
textXl: "clamp(0.825rem, 1.21vw, 1.1rem)",
text2xl: "clamp(0.975rem, 1.43vw, 1.3rem)",
text3xl: "clamp(1.2rem, 1.76vw, 1.6rem)",
text4xl: "clamp(1.5rem, 2.2vw, 2rem)",
text5xl: "clamp(2.025rem, 3.025vw, 2.75rem)",
text6xl: "clamp(2.475rem, 3.63vw, 3.3rem)",
text7xl: "clamp(3rem, 4.4vw, 4rem)",
text8xl: "clamp(3.5rem, 4.95vw, 4.5rem)",
text9xl: "clamp(5.25rem, 7.7vw, 7rem)",
},
mobile: {
text2xs: "2.75vw",
textXs: "3.025vw",
textSm: "3.3vw",
textBase: "3.575vw",
textLg: "3.85vw",
textXl: "4.675vw",
text2xl: "5.5vw",
text3xl: "6.6vw",
text4xl: "7.7vw",
text5xl: "8.25vw",
text6xl: "9.35vw",
text7xl: "11vw",
text8xl: "13.2vw",
text9xl: "15.4vw",
},
},
large: {
desktop: {
text2xs: "clamp(0.465rem, 0.713vw, 0.62rem)",
textXs: "clamp(0.54rem, 0.828vw, 0.72rem)",
textSm: "clamp(0.615rem, 0.943vw, 0.82rem)",
textBase: "clamp(0.69rem, 1.058vw, 0.92rem)",
textLg: "clamp(0.75rem, 1.15vw, 1rem)",
textXl: "clamp(0.825rem, 1.265vw, 1.1rem)",
text2xl: "clamp(0.975rem, 1.495vw, 1.3rem)",
text3xl: "clamp(1.2rem, 1.84vw, 1.6rem)",
text4xl: "clamp(1.5rem, 2.3vw, 2rem)",
text5xl: "clamp(2.025rem, 3.1625vw, 2.75rem)",
text6xl: "clamp(2.475rem, 3.795vw, 3.3rem)",
text7xl: "clamp(3rem, 4.6vw, 4rem)",
text8xl: "clamp(3.5rem, 5.175vw, 4.5rem)",
text9xl: "clamp(5.25rem, 8.05vw, 7rem)",
},
mobile: {
text2xs: "2.875vw",
textXs: "3.1625vw",
textSm: "3.45vw",
textBase: "3.7375vw",
textLg: "4.025vw",
textXl: "4.8875vw",
text2xl: "5.75vw",
text3xl: "6.9vw",
text4xl: "8.05vw",
text5xl: "8.625vw",
text6xl: "9.775vw",
text7xl: "11.5vw",
text8xl: "13.8vw",
text9xl: "16.1vw",
},
},
mediumSizeLargeTitles: {
desktop: {
// Medium body text sizes: use medium values
text2xs: "clamp(0.465rem, 0.62vw, 0.62rem)",
textXs: "clamp(0.54rem, 0.72vw, 0.72rem)",
textSm: "clamp(0.615rem, 0.82vw, 0.82rem)",
textBase: "clamp(0.69rem, 0.92vw, 0.92rem)",
textLg: "clamp(0.75rem, 1vw, 1rem)",
textXl: "clamp(0.825rem, 1.1vw, 1.1rem)",
text2xl: "clamp(0.975rem, 1.3vw, 1.3rem)",
text3xl: "clamp(1.2rem, 1.6vw, 1.6rem)",
text4xl: "clamp(1.5rem, 2vw, 2rem)",
// Large title sizes: use large preset values
text5xl: "clamp(2.025rem, 3.1625vw, 2.75rem)",
text6xl: "clamp(2.475rem, 3.795vw, 3.3rem)",
text7xl: "clamp(3rem, 4.6vw, 4rem)",
text8xl: "clamp(3.5rem, 5.175vw, 4.5rem)",
text9xl: "clamp(5.25rem, 8.05vw, 7rem)",
},
mobile: {
// Medium body text sizes: use medium values
text2xs: "2.5vw",
textXs: "2.75vw",
textSm: "3vw",
textBase: "3.25vw",
textLg: "3.5vw",
textXl: "4.25vw",
text2xl: "5vw",
text3xl: "6vw",
text4xl: "7vw",
// Large title sizes: use large preset values
text5xl: "8.625vw",
text6xl: "9.775vw",
text7xl: "11.5vw",
text8xl: "13.8vw",
text9xl: "16.1vw",
},
},
largeSizeMediumTitles: {
desktop: {
// Large body text sizes: use large values
text2xs: "clamp(0.465rem, 0.713vw, 0.62rem)",
textXs: "clamp(0.54rem, 0.828vw, 0.72rem)",
textSm: "clamp(0.615rem, 0.943vw, 0.82rem)",
textBase: "clamp(0.69rem, 1.058vw, 0.92rem)",
textLg: "clamp(0.75rem, 1.15vw, 1rem)",
textXl: "clamp(0.825rem, 1.265vw, 1.1rem)",
text2xl: "clamp(0.975rem, 1.495vw, 1.3rem)",
text3xl: "clamp(1.2rem, 1.84vw, 1.6rem)",
text4xl: "clamp(1.5rem, 2.3vw, 2rem)",
// Medium title sizes: use medium preset values
text5xl: "clamp(2.025rem, 2.75vw, 2.75rem)",
text6xl: "clamp(2.475rem, 3.3vw, 3.3rem)",
text7xl: "clamp(3rem, 4vw, 4rem)",
text8xl: "clamp(3.5rem, 4.5vw, 4.5rem)",
text9xl: "clamp(5.25rem, 7vw, 7rem)",
},
mobile: {
// Large body text sizes: use large values
text2xs: "2.875vw",
textXs: "3.1625vw",
textSm: "3.45vw",
textBase: "3.7375vw",
textLg: "4.025vw",
textXl: "4.8875vw",
text2xl: "5.75vw",
text3xl: "6.9vw",
text4xl: "8.05vw",
// Medium title sizes: use medium preset values
text5xl: "7.5vw",
text6xl: "8.5vw",
text7xl: "10vw",
text8xl: "12vw",
text9xl: "14vw",
},
},
mediumLargeSizeLargeTitles: {
desktop: {
// MediumLarge body text sizes
text2xs: "clamp(0.465rem, 0.651vw, 0.62rem)",
textXs: "clamp(0.54rem, 0.756vw, 0.72rem)",
textSm: "clamp(0.615rem, 0.861vw, 0.82rem)",
textBase: "clamp(0.69rem, 0.966vw, 0.92rem)",
textLg: "clamp(0.75rem, 1.05vw, 1rem)",
textXl: "clamp(0.825rem, 1.155vw, 1.1rem)",
text2xl: "clamp(0.975rem, 1.365vw, 1.3rem)",
text3xl: "clamp(1.2rem, 1.68vw, 1.6rem)",
text4xl: "clamp(1.5rem, 2.1vw, 2rem)",
// Large title sizes
text5xl: "clamp(2.025rem, 3.1625vw, 2.75rem)",
text6xl: "clamp(2.475rem, 3.795vw, 3.3rem)",
text7xl: "clamp(3rem, 4.6vw, 4rem)",
text8xl: "clamp(3.5rem, 5.175vw, 4.5rem)",
text9xl: "clamp(5.25rem, 8.05vw, 7rem)",
},
mobile: {
// MediumLarge body text sizes
text2xs: "2.625vw",
textXs: "2.8875vw",
textSm: "3.15vw",
textBase: "3.4125vw",
textLg: "3.675vw",
textXl: "4.4625vw",
text2xl: "5.25vw",
text3xl: "6.3vw",
text4xl: "7.35vw",
// Large title sizes
text5xl: "8.625vw",
text6xl: "9.775vw",
text7xl: "11.5vw",
text8xl: "13.8vw",
text9xl: "16.1vw",
},
},
largeSmallSizeLargeTitles: {
desktop: {
// LargeSmall body text sizes
text2xs: "clamp(0.465rem, 0.682vw, 0.62rem)",
textXs: "clamp(0.54rem, 0.792vw, 0.72rem)",
textSm: "clamp(0.615rem, 0.902vw, 0.82rem)",
textBase: "clamp(0.69rem, 1.012vw, 0.92rem)",
textLg: "clamp(0.75rem, 1.1vw, 1rem)",
textXl: "clamp(0.825rem, 1.21vw, 1.1rem)",
text2xl: "clamp(0.975rem, 1.43vw, 1.3rem)",
text3xl: "clamp(1.2rem, 1.76vw, 1.6rem)",
text4xl: "clamp(1.5rem, 2.2vw, 2rem)",
// Large title sizes
text5xl: "clamp(2.025rem, 3.1625vw, 2.75rem)",
text6xl: "clamp(2.475rem, 3.795vw, 3.3rem)",
text7xl: "clamp(3rem, 4.6vw, 4rem)",
text8xl: "clamp(3.5rem, 5.175vw, 4.5rem)",
text9xl: "clamp(5.25rem, 8.05vw, 7rem)",
},
mobile: {
// LargeSmall body text sizes
text2xs: "2.75vw",
textXs: "3.025vw",
textSm: "3.3vw",
textBase: "3.575vw",
textLg: "3.85vw",
textXl: "4.675vw",
text2xl: "5.5vw",
text3xl: "6.6vw",
text4xl: "7.7vw",
// Large title sizes
text5xl: "8.625vw",
text6xl: "9.775vw",
text7xl: "11.5vw",
text8xl: "13.8vw",
text9xl: "16.1vw",
},
},
mediumLargeSizeMediumTitles: {
desktop: {
// MediumLarge body text sizes
text2xs: "clamp(0.465rem, 0.651vw, 0.62rem)",
textXs: "clamp(0.54rem, 0.756vw, 0.72rem)",
textSm: "clamp(0.615rem, 0.861vw, 0.82rem)",
textBase: "clamp(0.69rem, 0.966vw, 0.92rem)",
textLg: "clamp(0.75rem, 1.05vw, 1rem)",
textXl: "clamp(0.825rem, 1.155vw, 1.1rem)",
text2xl: "clamp(0.975rem, 1.365vw, 1.3rem)",
text3xl: "clamp(1.2rem, 1.68vw, 1.6rem)",
text4xl: "clamp(1.5rem, 2.1vw, 2rem)",
// Medium title sizes
text5xl: "clamp(2.025rem, 2.75vw, 2.75rem)",
text6xl: "clamp(2.475rem, 3.3vw, 3.3rem)",
text7xl: "clamp(3rem, 4vw, 4rem)",
text8xl: "clamp(3.5rem, 4.5vw, 4.5rem)",
text9xl: "clamp(5.25rem, 7vw, 7rem)",
},
mobile: {
// MediumLarge body text sizes
text2xs: "2.625vw",
textXs: "2.8875vw",
textSm: "3.15vw",
textBase: "3.4125vw",
textLg: "3.675vw",
textXl: "4.4625vw",
text2xl: "5.25vw",
text3xl: "6.3vw",
text4xl: "7.35vw",
// Medium title sizes
text5xl: "7.5vw",
text6xl: "8.5vw",
text7xl: "10vw",
text8xl: "12vw",
text9xl: "14vw",
},
},
largeSmallSizeMediumTitles: {
desktop: {
// LargeSmall body text sizes
text2xs: "clamp(0.465rem, 0.682vw, 0.62rem)",
textXs: "clamp(0.54rem, 0.792vw, 0.72rem)",
textSm: "clamp(0.615rem, 0.902vw, 0.82rem)",
textBase: "clamp(0.69rem, 1.012vw, 0.92rem)",
textLg: "clamp(0.75rem, 1.1vw, 1rem)",
textXl: "clamp(0.825rem, 1.21vw, 1.1rem)",
text2xl: "clamp(0.975rem, 1.43vw, 1.3rem)",
text3xl: "clamp(1.2rem, 1.76vw, 1.6rem)",
text4xl: "clamp(1.5rem, 2.2vw, 2rem)",
// Medium title sizes
text5xl: "clamp(2.025rem, 2.75vw, 2.75rem)",
text6xl: "clamp(2.475rem, 3.3vw, 3.3rem)",
text7xl: "clamp(3rem, 4vw, 4rem)",
text8xl: "clamp(3.5rem, 4.5vw, 4.5rem)",
text9xl: "clamp(5.25rem, 7vw, 7rem)",
},
mobile: {
// LargeSmall body text sizes
text2xs: "2.75vw",
textXs: "3.025vw",
textSm: "3.3vw",
textBase: "3.575vw",
textLg: "3.85vw",
textXl: "4.675vw",
text2xl: "5.5vw",
text3xl: "6.6vw",
text4xl: "7.7vw",
// Medium title sizes
text5xl: "7.5vw",
text6xl: "8.5vw",
text7xl: "10vw",
text8xl: "12vw",
text9xl: "14vw",
},
},
};
interface BackgroundComponentProps {
className?: string;
invertColors?: boolean; // For AnimatedAuroraBackground
}
export const backgroundComponents: Record<BackgroundType, React.ComponentType<BackgroundComponentProps> | null> = {
none: null,
plain: PlainBackground,
grid: GridBackround,
dotGrid: DotGridBackground,
circleGradient: CircleGradientBackground,
aurora: AuroraBackground,
floatingGradient: FloatingGradientBackground,
animatedGrid: AnimatedGridBackground,
animatedAurora: AnimatedAuroraBackground,
fluid: FluidBackground,
radialGradient: RadialGradientBackground,
noise: NoiseBackground,
noiseGradient: NoiseGradientBackground,
noiseDiagonalGradient: NoiseDiagonalGradientBackground,
};

View File

@@ -0,0 +1,53 @@
import type { CTAButtonVariant } from "@/components/button/types";
import type { AnimationType } from "@/components/text/types";
export type BorderRadiusPreset = "sharp" | "rounded" | "soft" | "pill";
export type ContentWidthPreset = "small" | "smallMedium" | "compact" | "mediumSmall" | "medium" | "mediumLarge";
export type SizingPreset = "medium" | "mediumLarge" | "largeSmall" | "large" | "mediumSizeLargeTitles" | "mediumLargeSizeLargeTitles" | "largeSmallSizeLargeTitles" | "largeSizeMediumTitles" | "mediumLargeSizeMediumTitles" | "largeSmallSizeMediumTitles";
export type HeadingFontWeight = "light" | "normal" | "medium" | "semibold" | "bold" | "extrabold";
export type BackgroundType =
| "none"
| "plain"
| "grid"
| "dotGrid"
| "circleGradient"
| "aurora"
| "floatingGradient"
| "animatedGrid"
| "animatedAurora"
| "fluid"
| "radialGradient"
| "noise"
| "noiseGradient"
| "noiseDiagonalGradient";
export type CardStyleVariant = "solid" | "solid-accent" | "solid-accent-light" | "outline" | "outline-light" | "elevated" | "elevated-accent" | "elevated-accent-light" | "gradient-subtle" | "gradient-mesh" | "gradient-radial" | "neon-glow" | "inset" | "spotlight" | "shadow-colored" | "glass-elevated" | "glass-depth" | "gradient-bordered" | "layered-gradient";
export type PrimaryButtonStyleVariant = "gradient" | "shadow" | "flat" | "layered-depth" | "radial-glow" | "diagonal-gradient" | "neon-glow-border" | "outline";
export type SecondaryButtonStyleVariant = "glass" | "outline" | "solid" | "minimal" | "layered" | "radial-glow";
export interface ThemeConfig {
defaultButtonVariant: CTAButtonVariant;
defaultTextAnimation: AnimationType;
borderRadius: BorderRadiusPreset;
contentWidth: ContentWidthPreset;
sizing: SizingPreset;
background: BackgroundType;
cardStyle: CardStyleVariant;
primaryButtonStyle: PrimaryButtonStyleVariant;
secondaryButtonStyle: SecondaryButtonStyleVariant;
headingFontWeight: HeadingFontWeight;
}
export interface ThemeProviderProps {
children: React.ReactNode;
defaultButtonVariant: CTAButtonVariant;
defaultTextAnimation: AnimationType;
borderRadius: BorderRadiusPreset;
contentWidth: ContentWidthPreset;
sizing: SizingPreset;
background: BackgroundType;
cardStyle: CardStyleVariant;
primaryButtonStyle: PrimaryButtonStyleVariant;
secondaryButtonStyle: SecondaryButtonStyleVariant;
headingFontWeight: HeadingFontWeight;
}

View File

@@ -0,0 +1,142 @@
import type { CardStyleVariant } from "../config/types";
export const cardStyleMap: Record<CardStyleVariant, string> = {
"solid": `
position: relative;
background: var(--color-card);
`,
"solid-accent": `
position: relative;
background: color-mix(in srgb, var(--color-accent) 30%, transparent);
`,
"solid-accent-light": `
position: relative;
background: color-mix(in srgb, var(--color-accent) 15%, transparent);
`,
"outline": `
position: relative;
background: var(--color-card);
border: 1px solid color-mix(in srgb, var(--color-accent) 45%, transparent);
`,
"outline-light": `
position: relative;
background: var(--color-card);
border: 1px solid color-mix(in srgb, var(--color-accent) 15%, transparent);
`,
"elevated": `
position: relative;
background: var(--color-card);
box-shadow: 0 4px 6px -1px color-mix(in srgb, var(--color-foreground) 10%, transparent), 0 2px 4px -2px color-mix(in srgb, var(--color-foreground) 10%, transparent);
`,
"elevated-accent": `
position: relative;
background: color-mix(in srgb, var(--color-accent) 30%, transparent);
box-shadow: 0 4px 6px -1px color-mix(in srgb, var(--color-foreground) 10%, transparent), 0 2px 4px -2px color-mix(in srgb, var(--color-foreground) 10%, transparent);
`,
"elevated-accent-light": `
position: relative;
background: color-mix(in srgb, var(--color-accent) 15%, transparent);
box-shadow: 0 4px 6px -1px color-mix(in srgb, var(--color-foreground) 10%, transparent), 0 2px 4px -2px color-mix(in srgb, var(--color-foreground) 10%, transparent);
`,
"gradient-subtle": `
position: relative;
background: linear-gradient(135deg, var(--color-card) 0%, color-mix(in srgb, var(--color-card) 95%, var(--color-accent) 5%) 100%);
`,
"gradient-mesh": `
position: relative;
background:
radial-gradient(at 0% 0%, color-mix(in srgb, var(--color-accent) 15%, transparent) 0px, transparent 50%),
radial-gradient(at 100% 0%, color-mix(in srgb, var(--color-accent) 10%, transparent) 0px, transparent 50%),
radial-gradient(at 100% 100%, color-mix(in srgb, var(--color-accent) 20%, transparent) 0px, transparent 50%),
radial-gradient(at 0% 100%, color-mix(in srgb, var(--color-accent) 12%, transparent) 0px, transparent 50%),
var(--color-card);
`,
"gradient-radial": `
position: relative;
background: radial-gradient(circle at center, color-mix(in srgb, var(--color-card) 100%, var(--color-accent) 8%) 0%, var(--color-card) 70%);
`,
"neon-glow": `
position: relative;
background: var(--color-card);
box-shadow:
0 0 20px color-mix(in srgb, var(--color-accent) 15%, transparent),
0 0 40px color-mix(in srgb, var(--color-accent) 10%, transparent),
0 0 60px color-mix(in srgb, var(--color-accent) 5%, transparent);
border: 1px solid color-mix(in srgb, var(--color-accent) 30%, transparent);
`,
"inset": `
position: relative;
background: color-mix(in srgb, var(--color-card) 95%, var(--color-accent) 5%);
box-shadow:
inset 2px 2px 4px color-mix(in srgb, var(--color-accent) 15%, transparent),
inset -2px -2px 4px color-mix(in srgb, var(--color-background) 20%, transparent);
`,
"spotlight": `
position: relative;
background: radial-gradient(ellipse at top, color-mix(in srgb, var(--color-card) 100%, var(--color-accent) 12%) 0%, var(--color-card) 60%);
`,
"shadow-colored": `
position: relative;
background: var(--color-card);
box-shadow: 0 4px 6px -1px color-mix(in srgb, var(--color-accent) 20%, transparent), 0 2px 4px -2px color-mix(in srgb, var(--color-accent) 20%, transparent);
`,
"glass-elevated": `
position: relative;
backdrop-filter: blur(8px);
background: linear-gradient(to bottom right, color-mix(in srgb, var(--color-card) 80%, transparent), color-mix(in srgb, var(--color-card) 40%, transparent));
box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
border: 1px solid var(--color-card);
`,
"glass-depth": `
position: relative;
background: color-mix(in srgb, var(--color-card) 80%, transparent);
backdrop-filter: blur(14px);
box-shadow:
inset 0 0 20px 0 color-mix(in srgb, var(--color-accent) 7.5%, transparent);
border: 1px solid color-mix(in srgb, var(--color-accent) 7.5%, transparent);
`,
"gradient-bordered": `
position: relative;
background: linear-gradient(180deg, color-mix(in srgb, var(--color-card) 100%, var(--color-accent) 5%) -35%, var(--color-card) 65%);
box-shadow: 0px 0px 10px 4px color-mix(in srgb, var(--color-accent) 4%, transparent);
`,
"layered-gradient": `
position: relative;
background:
linear-gradient(color-mix(in srgb, var(--color-accent) 6%, transparent) 0%, transparent 59.26%),
linear-gradient(var(--color-card) 0%, var(--color-card) 100%),
var(--color-card);
box-shadow:
20px 18px 7px color-mix(in srgb, var(--color-accent) 0%, transparent),
2px 2px 2px color-mix(in srgb, var(--color-accent) 6.5%, transparent),
1px 1px 2px color-mix(in srgb, var(--color-accent) 2%, transparent);
border: 2px solid var(--color-secondary-cta);
`,
};
export const getGradientBorderedPseudo = (cardStyle: CardStyleVariant): string => {
if (cardStyle !== "gradient-bordered") return '';
return `
.card::before {
content: "";
position: absolute;
pointer-events: none;
inset: 0;
padding: 1px;
border-radius: inherit;
background: linear-gradient(
160deg,
color-mix(in srgb, var(--color-accent) 25%, transparent) 0%,
color-mix(in srgb, var(--color-accent) 5%, transparent) 35%,
color-mix(in srgb, var(--color-foreground) 5%, transparent) 75%,
color-mix(in srgb, var(--color-background-accent) 15%, transparent) 100%
);
-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
-webkit-mask-composite: xor;
mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
mask-composite: exclude;
}
`;
};

View File

@@ -0,0 +1,48 @@
import type { PrimaryButtonStyleVariant } from "../config/types";
export const primaryButtonStyleMap: Record<PrimaryButtonStyleVariant, string> = {
gradient: `
background: linear-gradient(to bottom, color-mix(in srgb, var(--color-primary-cta) 83%, transparent), var(--color-primary-cta));
box-shadow: color-mix(in srgb, var(--color-background) 25%, transparent) 0px 1px 1px 0px inset, color-mix(in srgb, var(--color-primary-cta) 15%, transparent) 3px 3px 3px 0px;
`,
shadow: `
background: var(--color-primary-cta);
box-shadow: 2.10837px 3.16256px 9.48767px color-mix(in srgb, var(--color-accent) 40%, transparent);
`,
flat: `
background: var(--color-primary-cta);
box-shadow: none;
`,
"layered-depth": `
background: linear-gradient(to bottom, color-mix(in srgb, var(--color-primary-cta) 100%, #ffffff 20%), color-mix(in srgb, var(--color-primary-cta) 100%, #000000 40%));
box-shadow:
2.10837px 3.16256px 9.48767px color-mix(in srgb, var(--color-accent) 40%, transparent),
inset 0 0 1.05419px 3.16256px color-mix(in srgb, var(--color-secondary-cta) 0%, transparent),
inset 0 6.32511px 5.27093px color-mix(in srgb, var(--color-accent) 50%, transparent);
border: 1px solid color-mix(in srgb, var(--color-primary-cta) 100%, #000000 60%);
`,
"radial-glow": `
background:
radial-gradient(circle at 0% 0%, color-mix(in srgb, var(--color-accent) 40%, transparent) 0%, transparent 45%),
radial-gradient(circle at 100% 100%, color-mix(in srgb, var(--color-accent) 40%, transparent) 0%, transparent 45%),
var(--color-foreground);
box-shadow: 2.10837px 3.16256px 9.48767px color-mix(in srgb, var(--color-accent) 30%, transparent);
`,
"diagonal-gradient": `
background: linear-gradient(to bottom right, color-mix(in srgb, var(--color-primary-cta) 65%, transparent), var(--color-accent));
box-shadow: 2.10837px 3.16256px 9.48767px color-mix(in srgb, var(--color-accent) 30%, transparent);
`,
"neon-glow-border": `
background: var(--color-primary-cta);
border: 2px solid var(--color-accent);
box-shadow:
0 0 10px color-mix(in srgb, var(--color-accent) 40%, transparent),
0 0 20px color-mix(in srgb, var(--color-accent) 20%, transparent),
inset 0 0 10px color-mix(in srgb, var(--color-accent) 15%, transparent);
`,
"outline": `
background: var(--color-foreground);
border: 2px solid var(--color-accent);
box-shadow: none;
`,
};

View File

@@ -0,0 +1,43 @@
import type { SecondaryButtonStyleVariant } from "../config/types";
export const secondaryButtonStyleMap: Record<SecondaryButtonStyleVariant, string> = {
glass: `
backdrop-filter: blur(8px);
background: linear-gradient(to bottom right, color-mix(in srgb, var(--color-secondary-cta) 80%, transparent), var(--color-secondary-cta));
box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
border: 1px solid var(--color-secondary-cta);
`,
outline: `
background: var(--color-secondary-cta);
box-shadow: none;
border: 1px solid color-mix(in srgb, var(--color-accent) 15%, transparent);
`,
solid: `
background: var(--color-secondary-cta);
box-shadow: none;
`,
minimal: `
background: color-mix(in srgb, var(--color-secondary-cta) 50%, transparent);
box-shadow: none;
border: 0;
`,
layered: `
background:
linear-gradient(color-mix(in srgb, var(--color-accent) 5%, transparent) 0%, transparent 59.26%),
linear-gradient(var(--color-secondary-cta), var(--color-secondary-cta)),
linear-gradient(var(--color-secondary-cta), var(--color-secondary-cta)),
linear-gradient(color-mix(in srgb, var(--color-accent) 5%, transparent) 0%, transparent 59.26%),
linear-gradient(color-mix(in srgb, var(--color-secondary-cta) 60%, transparent), color-mix(in srgb, var(--color-secondary-cta) 60%, transparent)),
var(--color-secondary-cta);
box-shadow:
2.10837px 3.16256px 9.48767px color-mix(in srgb, var(--color-accent) 10%, transparent);
border: 1px solid var(--color-secondary-cta);
`,
"radial-glow": `
background:
radial-gradient(circle at 0% 0%, color-mix(in srgb, var(--color-accent) 15%, transparent) 0%, transparent 40%),
radial-gradient(circle at 100% 100%, color-mix(in srgb, var(--color-accent) 15%, transparent) 0%, transparent 40%),
var(--color-secondary-cta);
box-shadow: 2.10837px 3.16256px 9.48767px color-mix(in srgb, var(--color-accent) 10%, transparent);
`,
};

View File

@@ -0,0 +1,21 @@
/**
* Detects if the current background color is light or dark
* Used for AnimatedAuroraBackground to determine if colors should be inverted
*
* @returns true for light backgrounds (hex starting with 8-f), false for dark backgrounds (hex starting with 0-7)
*/
export const detectLightBackground = (): boolean => {
if (typeof window === 'undefined') return true;
const computedBg = getComputedStyle(document.documentElement)
.getPropertyValue('--background')
.trim();
// If it starts with #0-7, it's likely dark
if (computedBg.startsWith('#')) {
const firstChar = computedBg.charAt(1).toLowerCase();
return firstChar >= '8'; // 8-f is light, 0-7 is dark
}
return true; // Default to light
};