From 8cc96564e1c5531c999fe91d77b38aa24940dd74 Mon Sep 17 00:00:00 2001 From: vchikalkin Date: Sun, 8 Dec 2024 21:16:28 +0300 Subject: [PATCH] beautify --- app/layout.tsx | 2 +- app/page.tsx | 17 +-- components/ui/neon-gradient-card.tsx | 151 +++++++++++++++++++++++++++ tailwind.config.ts | 13 +++ 4 files changed, 176 insertions(+), 7 deletions(-) create mode 100644 components/ui/neon-gradient-card.tsx diff --git a/app/layout.tsx b/app/layout.tsx index a38b174..1ff1e25 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -18,7 +18,7 @@ export default async function RootLayout({ children }: { readonly children: Reac {children} diff --git a/app/page.tsx b/app/page.tsx index 97ed30c..c02b961 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -2,6 +2,7 @@ import { About } from '@/components/about'; import { Contacts } from '@/components/contacts'; import { Person } from '@/components/person'; import { Skills } from '@/components/skills'; +import { NeonGradientCard } from '@/components/ui/neon-gradient-card'; import { Work } from '@/components/work'; import { type Metadata } from 'next'; import { getTranslations } from 'next-intl/server'; @@ -22,12 +23,16 @@ export async function generateMetadata({ params: { locale } }: Parameters): Prom export default function HomePage() { return ( -
- - - - - +
+ +
+ + + + + +
+
); } diff --git a/components/ui/neon-gradient-card.tsx b/components/ui/neon-gradient-card.tsx new file mode 100644 index 0000000..3192789 --- /dev/null +++ b/components/ui/neon-gradient-card.tsx @@ -0,0 +1,151 @@ +"use client"; + +import { + CSSProperties, + ReactElement, + ReactNode, + useEffect, + useRef, + useState, +} from "react"; + +import { cn } from "@/lib/utils"; + +interface NeonColorsProps { + firstColor: string; + secondColor: string; +} + +interface NeonGradientCardProps { + /** + * @default
+ * @type ReactElement + * @description + * The component to be rendered as the card + * */ + as?: ReactElement; + /** + * @default "" + * @type string + * @description + * The className of the card + */ + className?: string; + + /** + * @default "" + * @type ReactNode + * @description + * The children of the card + * */ + children?: ReactNode; + + /** + * @default 5 + * @type number + * @description + * The size of the border in pixels + * */ + borderSize?: number; + + /** + * @default 20 + * @type number + * @description + * The size of the radius in pixels + * */ + borderRadius?: number; + + /** + * @default "{ firstColor: '#ff00aa', secondColor: '#00FFF1' }" + * @type string + * @description + * The colors of the neon gradient + * */ + neonColors?: NeonColorsProps; + + [key: string]: any; +} + +const NeonGradientCard: React.FC = ({ + className, + children, + borderSize = 2, + borderRadius = 20, + neonColors = { + firstColor: "#ff00aa", + secondColor: "#00FFF1", + }, + ...props +}) => { + const containerRef = useRef(null); + const [dimensions, setDimensions] = useState({ width: 0, height: 0 }); + + useEffect(() => { + const updateDimensions = () => { + if (containerRef.current) { + const { offsetWidth, offsetHeight } = containerRef.current; + setDimensions({ width: offsetWidth, height: offsetHeight }); + } + }; + + updateDimensions(); + window.addEventListener("resize", updateDimensions); + + return () => { + window.removeEventListener("resize", updateDimensions); + }; + }, []); + + useEffect(() => { + if (containerRef.current) { + const { offsetWidth, offsetHeight } = containerRef.current; + setDimensions({ width: offsetWidth, height: offsetHeight }); + } + }, [children]); + + return ( +
+
+ {children} +
+
+ ); +}; + +export { NeonGradientCard }; diff --git a/tailwind.config.ts b/tailwind.config.ts index 773b1e6..e55b8e6 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -55,6 +55,19 @@ export default { lg: 'var(--radius)', md: 'calc(var(--radius) - 2px)', sm: 'calc(var(--radius) - 4px)' + }, + animation: { + 'background-position-spin': 'background-position-spin 3000ms infinite alternate' + }, + keyframes: { + 'background-position-spin': { + '0%': { + backgroundPosition: 'top center' + }, + '100%': { + backgroundPosition: 'bottom center' + } + } } } },