diff --git a/apps/web/app/global.css b/apps/web/app/global.css deleted file mode 100644 index 1b8feb48..00000000 --- a/apps/web/app/global.css +++ /dev/null @@ -1,17 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -* { - margin: 0; - padding: 0; -} - -.unselectable { - user-drag: none; - user-select: none; - -moz-user-select: none; - -webkit-user-drag: none; - -webkit-user-select: none; - -ms-user-select: none; -} \ No newline at end of file diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx deleted file mode 100644 index dab394e7..00000000 --- a/apps/web/app/page.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { Poppins } from 'next/font/google' -import Links from '../components/links' -import Killers from '../components/killers' -import { Logo, Grid, Stars } from '../public' - -const poppins = Poppins({ - subsets: ['latin'], - weight: ['400', '500', '600', '700'] -}) - -function Index(): React.JSX.Element { - return ( -
-
-
-
-
- - -
-
-
- -

- keyshade.xyz -

-
-
-

- Manage all your secrets securely with public key encryption and - realtime based tools, that seamlessly fits into your codebase -

-
- -
- - -
- -
- - -
-
-
-
-
- ) -} - -export default Index diff --git a/apps/web/components/killers.tsx b/apps/web/components/killers.tsx deleted file mode 100644 index a2b77c60..00000000 --- a/apps/web/components/killers.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import Image from 'next/image' -import { Inter } from 'next/font/google' -import React from 'react' - -const inter = Inter({ subsets: ['latin'] }) - -interface KillersProps { - image: string - twitterUserName: string -} -function Killers({ image, twitterUserName }: KillersProps): React.JSX.Element { - return ( -
-
- {image} -
-
-

{image}

- -

@{twitterUserName}

-
-
-
- ) -} - -export default Killers diff --git a/apps/web/package.json b/apps/web/package.json index 3f96fb40..aeb7a99d 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -9,9 +9,14 @@ "lint": "next lint --fix" }, "dependencies": { + "clsx": "^2.1.0", + "framer-motion": "^11.0.8", + "jsonp": "^0.2.1", "next": "^13.5.6", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "sonner": "^1.4.3", + "tailwind-merge": "^2.2.1" }, "devDependencies": { "@next/eslint-plugin-next": "^13.4.19", diff --git a/apps/web/public/XSVG.svg b/apps/web/public/XSVG.svg new file mode 100644 index 00000000..8544187d --- /dev/null +++ b/apps/web/public/XSVG.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/web/public/discordSVG.svg b/apps/web/public/discordSVG.svg new file mode 100644 index 00000000..9284e6b1 --- /dev/null +++ b/apps/web/public/discordSVG.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/web/public/index.ts b/apps/web/public/index.ts index 04b8b91e..9f2abce8 100644 --- a/apps/web/public/index.ts +++ b/apps/web/public/index.ts @@ -3,3 +3,7 @@ export { default as Grid } from './grid.svg' export { default as Logo } from './logo.svg' export { default as XSvg } from './x_svg.svg' export { default as Stars } from './stars.svg' + +export {default as XSVG} from "./XSVG.svg" +export {default as LinkdinSVG} from "./linkdinSVG (1).svg" +export {default as DiscordSVG} from "./discordSVG.svg" diff --git a/apps/web/public/linkdinSVG (1).svg b/apps/web/public/linkdinSVG (1).svg new file mode 100644 index 00000000..aac8deab --- /dev/null +++ b/apps/web/public/linkdinSVG (1).svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/apps/web/src/app/global.css b/apps/web/src/app/global.css new file mode 100644 index 00000000..2a8abb88 --- /dev/null +++ b/apps/web/src/app/global.css @@ -0,0 +1,7 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +body { + background: #03080B; +} \ No newline at end of file diff --git a/apps/web/app/layout.tsx b/apps/web/src/app/layout.tsx similarity index 100% rename from apps/web/app/layout.tsx rename to apps/web/src/app/layout.tsx diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx new file mode 100644 index 00000000..03598f0f --- /dev/null +++ b/apps/web/src/app/page.tsx @@ -0,0 +1,152 @@ +'use client' + +import { Poppins } from 'next/font/google' +import { useState } from 'react' +import { Toaster, toast } from 'sonner' +import { InputBorderSpotlight } from '@/components/ui/input-spotlight' +import EncryptButton from '@/components/ui/encrypt-btn' +import Links from '../components/links' +import Killers from '../components/killers' +import { Logo, Grid, Stars, DiscordSVG, XSVG, LinkdinSVG } from '../../public' + +const poppins = Poppins({ + subsets: ['latin'], + weight: ['400', '500', '600', '700'] +}) + +function Index(): React.JSX.Element { + const [email, setEmail] = useState('') + + // eslint-disable-next-line no-console -- chill + console.log(email) + + const onSubmit = (e: React.FormEvent): void => { + e.preventDefault() + + if (email === '') { + toast.custom(() => ( +
+

Pleasse enter an email address

+
+ )) + return + } + + const url = + 'https://xyz.us18.list-manage.com/subscribe/post?u=2e44b940cafe6e54d8b9e0790&id=bd382dd7c5&f_id=00e5c2e1f0' + + async function fetchData(): Promise { + toast.custom((_t) => ( +
+

Welcome to Keyshade 🎉

+

+ You have been added to the waitlist. We will notify you once we + launch +

+ {/* */} +
+ )) + try { + await fetch(`${url}&EMAIL=${email}`, { + mode: 'no-cors' + }) + } catch (error) { + // eslint-disable-next-line no-console -- chill + console.error(error) + } + } + void fetchData() + } + + return ( + <> + +
+
+
+
+
+ + +
+
+
+ +

+ keyshade.xyz +

+
+
+

+ Manage all your secrets securely with public key encryption + and realtime based tools, that seamlessly fits into your + codebase +

+
+ +
+ + +
+
+
+ +
+ onSubmit} + /> +
+
+
+ + +
+
+
+
+ + ) +} + +export default Index diff --git a/apps/web/src/components/killers.tsx b/apps/web/src/components/killers.tsx new file mode 100644 index 00000000..7f3cef5f --- /dev/null +++ b/apps/web/src/components/killers.tsx @@ -0,0 +1,16 @@ +import React from 'react' + +interface KillersProps { + children: React.ReactNode +} +function Killers({ children }: KillersProps): React.JSX.Element { + return ( +
+
+ {children} +
+
+ ) +} + +export default Killers diff --git a/apps/web/components/links.tsx b/apps/web/src/components/links.tsx similarity index 100% rename from apps/web/components/links.tsx rename to apps/web/src/components/links.tsx diff --git a/apps/web/src/components/ui/encrypt-btn.tsx b/apps/web/src/components/ui/encrypt-btn.tsx new file mode 100644 index 00000000..875e8db0 --- /dev/null +++ b/apps/web/src/components/ui/encrypt-btn.tsx @@ -0,0 +1,104 @@ +'use client' +import React, { useRef, useState } from 'react' +import { motion } from 'framer-motion' + +interface EncryptButtonProps extends React.HTMLProps { + TARGET_TEXT: string +} + +function EncryptButton({ TARGET_TEXT }: EncryptButtonProps): React.JSX.Element { + // const TARGET_TEXT = 'Join Waitlist' + const CYCLES_PER_LETTER = 2 + const SHUFFLE_TIME = 50 + + const CHARS = '!@#$%^&*():{};|,.<>/?' + + const intervalRef = useRef | null>(null) + + const [text, setText] = useState(TARGET_TEXT) + + const scramble = (): void => { + let pos = 0 + + intervalRef.current = setInterval(() => { + const scrambled = TARGET_TEXT.split('') + .map((char, index) => { + if (pos / CYCLES_PER_LETTER > index) { + return char + } + + const randomCharIndex = Math.floor(Math.random() * CHARS.length) + const randomChar = CHARS[randomCharIndex] + + return randomChar + }) + .join('') + + setText(scrambled) + pos++ + + if (pos >= TARGET_TEXT.length * CYCLES_PER_LETTER) { + stopScramble() + } + }, SHUFFLE_TIME) + } + + const stopScramble = (): void => { + clearInterval(intervalRef.current || undefined) + + setText(TARGET_TEXT) + } + + return ( + +
+ + {text}{' '} + + + + +
+ +
+ ) +} + +export default EncryptButton diff --git a/apps/web/src/components/ui/input-spotlight.tsx b/apps/web/src/components/ui/input-spotlight.tsx new file mode 100644 index 00000000..af95d42a --- /dev/null +++ b/apps/web/src/components/ui/input-spotlight.tsx @@ -0,0 +1,77 @@ +'use client' +import type { Dispatch, SetStateAction } from 'react' +import React, { useRef, useState } from 'react' + +interface InputBorderSpotlightProps { + setEmail: Dispatch> +} + +export function InputBorderSpotlight({ + setEmail +}: InputBorderSpotlightProps): React.JSX.Element { + const divRef = useRef(null) + const [isFocused, setIsFocused] = useState(false) + const [position, setPosition] = useState({ x: 0, y: 0 }) + const [opacity, setOpacity] = useState(0) + + const handleMouseMove = (e: React.MouseEvent): void => { + if (!divRef.current || isFocused) return + + const div = divRef.current + const rect = div.getBoundingClientRect() + + setPosition({ x: e.clientX - rect.left, y: e.clientY - rect.top }) + } + + const handleFocus = (): void => { + setIsFocused(true) + setOpacity(1) + } + + const handleBlur = (): void => { + setIsFocused(false) + setOpacity(0) + } + + const handleMouseEnter = (): void => { + setOpacity(1) + } + + const handleMouseLeave = (): void => { + setOpacity(0) + } + + return ( +
+ { + setEmail(e.target.value) + }} + onFocus={handleFocus} + onMouseEnter={handleMouseEnter} + onMouseLeave={handleMouseLeave} + onMouseMove={handleMouseMove} + placeholder="Enter your email address" + size={25} + type="email" + /> + +
+ ) +} diff --git a/apps/web/src/components/ui/moving-border.tsx b/apps/web/src/components/ui/moving-border.tsx new file mode 100644 index 00000000..575a76a4 --- /dev/null +++ b/apps/web/src/components/ui/moving-border.tsx @@ -0,0 +1,149 @@ +/* eslint-disable @typescript-eslint/no-unsafe-assignment -- chill */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access -- chill */ +'use client' +import { useRef } from 'react' +import { + motion, + useAnimationFrame, + useMotionTemplate, + useMotionValue, + useTransform +} from 'framer-motion' +import { cn } from '@/utils/cn' + +export function Button({ + borderRadius = '9999px', + children, + as: Component = 'button', + containerClassName, + borderClassName, + duration, + className, + ...otherProps +}: { + borderRadius?: string + children: React.ReactNode + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- it's okey + as?: any + containerClassName?: string + borderClassName?: string + duration?: number + className?: string + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- it's okey + [key: string]: any +}): React.JSX.Element { + return ( + +
+ +
+ +
+ +
+

+ {children} +

+
+ + ) +} + +export function MovingBorder({ + children, + duration = 2000, + rx, + ry, + ...otherProps +}: { + children: React.ReactNode + duration?: number + rx?: string + ry?: string + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- it's + [key: string]: any +}): React.JSX.Element { + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- ok + const pathRef = useRef() + const progress = useMotionValue(0) + + useAnimationFrame((time) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-call -- chill + const length = pathRef.current?.getTotalLength() + if (length) { + const pxPerMillisecond = length / duration + progress.set((time * pxPerMillisecond) % length) + } + }) + + const x = useTransform( + progress, + // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call -- ignore + (val) => pathRef.current?.getPointAtLength(val).x + ) + const y = useTransform( + progress, + // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call -- ignore + (val) => pathRef.current?.getPointAtLength(val).y + ) + + const transform = useMotionTemplate`translateX(${x}px) translateY(${y}px) translateX(-50%) translateY(-50%)` + + return ( + <> + + + + + {children} + + + ) +} diff --git a/apps/web/src/utils/cn.ts b/apps/web/src/utils/cn.ts new file mode 100644 index 00000000..8b400cb8 --- /dev/null +++ b/apps/web/src/utils/cn.ts @@ -0,0 +1,7 @@ +import type { ClassValue } from 'clsx' +import { clsx } from 'clsx' +import { twMerge } from 'tailwind-merge' + +export function cn(...inputs: ClassValue[]): string { + return twMerge(clsx(inputs)) +} diff --git a/apps/web/tailwind.config.js b/apps/web/tailwind.config.js index d0b89854..b61c2997 100644 --- a/apps/web/tailwind.config.js +++ b/apps/web/tailwind.config.js @@ -1,60 +1,34 @@ -const plugin = require('tailwindcss/plugin') - -const radialGradientPlugin = plugin( - function ({ matchUtilities, theme }) { - matchUtilities( - { - // map to bg-radient-[*] - 'bg-radient': (value) => ({ - 'background-image': `radial-gradient(${value},var(--tw-gradient-stops))` - }) - }, - { values: theme('radialGradients') } - ) - }, - { - theme: { - radialGradients: _presets() - } - } -) - -/** - * utility class presets - */ -function _presets() { - const shapes = ['circle', 'ellipse'] - const pos = { - c: 'center', - t: 'top', - b: 'bottom', - l: 'left', - r: 'right', - tl: 'top left', - tr: 'top right', - bl: 'bottom left', - br: 'bottom right' - } - let result = {} - for (const shape of shapes) - for (const [posName, posValue] of Object.entries(pos)) - result[`${shape}-${posName}`] = `${shape} at ${posValue}` - - return result -} +const defaultTheme = require('tailwindcss/defaultTheme') +const colors = require('tailwindcss/colors') +const { + default: flattenColorPalette +} = require('tailwindcss/lib/util/flattenColorPalette') /** @type {import('tailwindcss').Config} */ module.exports = { - content: [ - './app/**/*.{js,ts,jsx,tsx,mdx}', - './pages/**/*.{js,ts,jsx,tsx,mdx}', - './components/**/*.{js,ts,jsx,tsx,mdx}', - - // Or if using `src` directory: - './src/**/*.{js,ts,jsx,tsx,mdx}' - ], + content: ['./src/**/*.{ts,tsx}'], + darkMode: 'class', theme: { - extend: {} + extend: { + colors: { + brandBlue: '#CAECF1' + } + } }, - plugins: [radialGradientPlugin] + plugins: [ + // rest of the code + addVariablesForColors + ] +} + +// This plugin adds each Tailwind color as a global CSS variable, e.g. var(--gray-200). +function addVariablesForColors({ addBase, theme }) { + let allColors = flattenColorPalette(theme('colors')) + let newVars = Object.fromEntries( + Object.entries(allColors).map(([key, val]) => [`--${key}`, val]) + ) + + addBase({ + ':root': newVars + }) }