diff options
Diffstat (limited to 'apps/website/src')
-rw-r--r-- | apps/website/src/components/base/Header.astro | 57 | ||||
-rw-r--r-- | apps/website/src/components/base/Navbar.astro | 21 | ||||
-rw-r--r-- | apps/website/src/components/base/NavbarElement.astro | 86 | ||||
-rw-r--r-- | apps/website/src/components/base/ScreenOverlay.astro | 16 | ||||
-rw-r--r-- | apps/website/src/components/base/ScrollbarOverlayContainer.astro | 51 | ||||
-rw-r--r-- | apps/website/src/components/base/Tag.astro | 14 | ||||
-rw-r--r-- | apps/website/src/components/icons/ChevronDown.astro | 16 | ||||
-rw-r--r-- | apps/website/src/components/logos/Logo.astro | 52 | ||||
-rw-r--r-- | apps/website/src/layouts/Layout.astro | 38 | ||||
-rw-r--r-- | apps/website/src/pages/index.astro | 10 | ||||
-rw-r--r-- | apps/website/src/styles/global.css | 11 | ||||
-rw-r--r-- | apps/website/src/types/Config.d.ts | 34 |
12 files changed, 406 insertions, 0 deletions
diff --git a/apps/website/src/components/base/Header.astro b/apps/website/src/components/base/Header.astro new file mode 100644 index 0000000..ad8267b --- /dev/null +++ b/apps/website/src/components/base/Header.astro @@ -0,0 +1,57 @@ +--- +import type { HTMLAttributes } from "astro/types" + +const sizes = { + "xl": "h1", + "lg": "h2", + "md": "h3", + "sm": "h4", + "xs": "h5", + "xxs": "h6" +}; + +type Headers = "h1" | "h2" | "h3" | "h4" | "h5" | "h6"; + +interface Props extends HTMLAttributes<Headers> { + size: keyof typeof sizes; +} + +const { size, ...attr } = Astro.props; +const Element = sizes[size] as any; // Unfortunately gotta do this +--- + +<Element {...attr}> + <slot /> +</Element> + +<style> + h1 { + font-size: theme("fontSize.header-lg"); + font-weight: 700; + } + + h2 { + font-size: theme("fontSize.header"); + font-weight: 700; + } + + h3 { + font-size: theme("fontSize.header-sm"); + font-weight: 600; + } + + h4 { + font-size: theme("fontSize.body-lg"); + font-weight: 500; + } + + h5 { + font-size: theme("fontSize.body"); + font-weight: 500; + } + + h6 { + font-size: theme("fontSize.body-sm"); + font-weight: 500; + } +</style>
\ No newline at end of file diff --git a/apps/website/src/components/base/Navbar.astro b/apps/website/src/components/base/Navbar.astro new file mode 100644 index 0000000..f4f2f49 --- /dev/null +++ b/apps/website/src/components/base/Navbar.astro @@ -0,0 +1,21 @@ +--- +import config from "config"; +import type { Config } from "@webtypes/Config"; +import NavbarElement from "./NavbarElement.astro"; + +--- + +<div class="w-full flex flex-row justify-center h-screen max-h-[110px] px-3"> + <nav class="w-full max-w-[1080px] flex flex-col md:flex-row justify-between items-center"> + <ul class="flex flex-row justify-start gap-4"> + {(config as Config).navbar.left.map((element, index) => ( + <NavbarElement {element} {index} /> + ))} + </ul> + <ul class="flex flex-row justify-end gap-4"> + {(config as Config).navbar.right.map((element, index) => ( + <NavbarElement {element} {index} /> + ))} + </ul> + </nav> +</div>
\ No newline at end of file diff --git a/apps/website/src/components/base/NavbarElement.astro b/apps/website/src/components/base/NavbarElement.astro new file mode 100644 index 0000000..4f82e8b --- /dev/null +++ b/apps/website/src/components/base/NavbarElement.astro @@ -0,0 +1,86 @@ +--- +import ChevronDown from "@components/icons/ChevronDown.astro"; +import type { LogoType, NavbarElement } from "@webtypes/Config"; +import ScreenOverlay from "./ScreenOverlay.astro"; +import Header from "./Header.astro"; +import Tag from "./Tag.astro"; +import Logo from "@components/logos/Logo.astro"; +import ScrollbarOverlayContainer from "./ScrollbarOverlayContainer.astro"; + +interface Props { + element: NavbarElement; + index: number; +} + +const { + element, + index +} = Astro.props; +--- + +<li class="sm:relative max-sm:overflow-hidden flex flex-row justify-center items-center text-center"> + <label for={`navbar-input-${index}`} class="group"> + + {element.path ? ( + <a href={element.path} class="p-2 flex flex-row justify-center items-center text-gray-700 hover:text-blue-500"> + {element.text && element.text} + {element.logo && <Logo size={element.logo[1] < 0 ? undefined : element.logo[1]} logo={element.logo[0] as LogoType} />} + {element.dropdown && <ChevronDown />} + </a> + ) : ( + <p class="p-2 flex flex-row justify-center items-center text-gray-700 hover:text-blue-500 cursor-default"> + {element.text && element.text} + {element.logo && <Logo size={element.logo[1] < 0 ? undefined : element.logo[1]} logo={element.logo[0] as LogoType} />} + {element.dropdown && <ChevronDown />} + </p> + )} + + {element.dropdown && ( + <ScreenOverlay class="max-sm:group-focus-within:opacity-100" /> + <input type="checkbox" id={`navbar-input-${index}`} class="peer appearance-none absolute"> + <div class={`transition-opacity + absolute right-0 max-sm:bottom-0 + max-sm:overflow-hidden + max-sm:max-h-[70vh] max-sm:h-screen max-sm:w-screen + pointer-events-none opacity-0 + + md:top-full md:right-0 + + group-focus-within:pointer-events-auto group-focus-within:opacity-100 + focus-within:pointer-events-auto focus-within:opacity-100 + hover:pointer-events-auto hover:opacity-100 + `}> + <ScrollbarOverlayContainer class={` + bg-gray-50 + rounded-t-lg md:rounded-lg + transition-transform + max-sm:translate-y-1/3 max-sm:left-0 + group-focus-within:translate-y-0 + max-h-full md:max-h-96 overflow-y-auto + `}> + <ul class="p-4"> + {element.dropdown.map((item) => ( + <li> + <a href={item.path} class="flex sm:min-w-[400px] sm:max-w-[400px]"> + <div class="transition-colors text-left w-full flex flex-row justify-start rounded-md items-center px-6 py-4 gap-6 hover:bg-white-hover"> + <div class="w-[36px]"> + {item.logo && <Logo size={40} logo={item.logo} />} + </div> + + <div class="flex flex-col justify-start items-start"> + <div class="flex flex-row gap-2"> + <Header size="sm" class="text-gray-800">{item.name}</Header> + {item.tag && <Tag>{item.tag}</Tag>} + </div> + <p class="text-sm text-gray-400 font-light">{item.description}</p> + </div> + </div> + </a> + </li> + ))} + </ul> + </ScrollbarOverlayContainer> + </div> + )} + </label> +</li>
\ No newline at end of file diff --git a/apps/website/src/components/base/ScreenOverlay.astro b/apps/website/src/components/base/ScreenOverlay.astro new file mode 100644 index 0000000..7be8bc5 --- /dev/null +++ b/apps/website/src/components/base/ScreenOverlay.astro @@ -0,0 +1,16 @@ +--- +import type { HTMLAttributes } from "astro/types"; + +interface Props extends HTMLAttributes<"div"> { + zIndex?: number; +} + +const { + zIndex = 0, + ...rest +} = Astro.props; +--- + +<div class="pointer-events-none absolute left-0 top-0"> + <div class={`transition-opacity fixed opacity-0 w-screen h-screen z-[${zIndex}] bg-black/30 ${rest.class}`} {...rest}></div> +</div>
\ No newline at end of file diff --git a/apps/website/src/components/base/ScrollbarOverlayContainer.astro b/apps/website/src/components/base/ScrollbarOverlayContainer.astro new file mode 100644 index 0000000..d8d315d --- /dev/null +++ b/apps/website/src/components/base/ScrollbarOverlayContainer.astro @@ -0,0 +1,51 @@ +--- +import type { HTMLAttributes } from "astro/types"; + +interface Props extends HTMLAttributes<"div"> { + +} + +const { ...attr } = Astro.props; +--- + +<div {...attr}> + <slot></slot> +</div> + +<style> +@media (hover: hover) { + div { + + } + + div::-webkit-scrollbar { + display: block; + width: 5px; + } + + div::-webkit-scrollbar:hover { + opacity: 1; + } + + div::-webkit-scrollbar-button { + display: none; + } + + div::-webkit-scrollbar-track { + background-color: #00000000; + } + + div::-webkit-scrollbar-track-piece { + background-color: #00000000; + } + + div::-webkit-scrollbar-thumb { + background-color: #00000000; + border-radius: 24px; + } + + div::-webkit-scrollbar-thumb:hover { + background-color: #00000040; + } +} +</style>
\ No newline at end of file diff --git a/apps/website/src/components/base/Tag.astro b/apps/website/src/components/base/Tag.astro new file mode 100644 index 0000000..e6fd34f --- /dev/null +++ b/apps/website/src/components/base/Tag.astro @@ -0,0 +1,14 @@ +--- +import type { HTMLAttributes } from "astro/types"; + +interface Props extends HTMLAttributes<"span"> {} + +const { ...attr } = Astro.props; +--- + +<div class="transition-colors text-blue-500 hover:bg-blue-500/20 text-xs font-medium bg-blue-500/10 rounded-md flex flex-col justify-center items-center px-2 py-0.5"> + <span {...attr}> + <slot></slot> + </span> +</div> + diff --git a/apps/website/src/components/icons/ChevronDown.astro b/apps/website/src/components/icons/ChevronDown.astro new file mode 100644 index 0000000..5cb98e4 --- /dev/null +++ b/apps/website/src/components/icons/ChevronDown.astro @@ -0,0 +1,16 @@ +--- +import type { HTMLAttributes } from 'astro/types'; + +interface Props extends HTMLAttributes<"svg"> { + size?: number; +} + +const { + size = 16, + ...attr +} = Astro.props; +--- + +<svg xmlns="http://www.w3.org/2000/svg" width={size} height={size} viewBox="0 0 16 16" fill="none" {...attr}> + <path d="M4 6L8 10L12 6" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"/> +</svg>
\ No newline at end of file diff --git a/apps/website/src/components/logos/Logo.astro b/apps/website/src/components/logos/Logo.astro new file mode 100644 index 0000000..0532396 --- /dev/null +++ b/apps/website/src/components/logos/Logo.astro @@ -0,0 +1,52 @@ +--- +import type { LogoType } from "@webtypes/Config"; +import { dirname } from "path"; +import { fileURLToPath } from "url"; +import type { HTMLAttributes } from "astro/types"; + +export interface Props extends HTMLAttributes<"svg"> { + logo: LogoType, + size?: number | [number, number], + silent?: boolean, +} + +const { + logo, + silent = false, + size = undefined, + ...attr +} = Astro.props; +let svg: string | undefined; + +try { + if (logo == undefined) return; + const dir = dirname(fileURLToPath(import.meta.url)) + "/../../../public/media"; + svg = (await import(`${dir}/${logo.replaceAll(".", "/")}.svg?raw` /* @vite-ignore */)).default; + + if (svg == undefined) return; + + if (typeof size == "number" || Array.isArray(size)) { + // SVG main element regex + const svgElementRegex = /<svg[^>]*>/; + + svg = svg.replace(svgElementRegex, (match) => { + let newMatch = match; + if (typeof size == "number") { + newMatch = newMatch.replace(/width="[^"]*"/, `width="${size}"`); + newMatch = newMatch.replace(/height="[^"]*"/, `height="${size}"`); + } else if (Array.isArray(size)) { + newMatch = newMatch.replace(/width="[^"]*"/, `width="${size[0]}"`); + newMatch = newMatch.replace(/height="[^"]*"/, `height="${size[1]}"`); + } + return newMatch; + }); + } + + svg = svg.replace(/<svg/, `<svg ${Object.entries(attr).map(([key, value]) => `${key}="${value}"`).join(" ")}`); +} catch (err) { + if (typeof silent != "boolean" || silent == false) console.error(err); +} + +--- + +<Fragment set:html={svg} /> diff --git a/apps/website/src/layouts/Layout.astro b/apps/website/src/layouts/Layout.astro new file mode 100644 index 0000000..a648f08 --- /dev/null +++ b/apps/website/src/layouts/Layout.astro @@ -0,0 +1,38 @@ +--- +import "../styles/global.css"; +import Navbar from "../components/base/Navbar.astro"; +import Favicon from "/media/polyfrost/minimal_bg.svg?url"; + +interface Props { + title?: string; + favicon?: string; +} + +const { + title = 'Polyfrost', + favicon = Favicon +} = Astro.props; +--- + +<!doctype html> +<html lang="en"> + <head> + <meta charset="UTF-8" /> + <meta name="description" content="Official website for Polyfrost." /> + <meta name="viewport" content="width=device-width" /> + <link rel="icon" type="image/svg+xml" href={favicon} /> + <meta name="generator" content={Astro.generator} /> + + <link rel="preconnect" href="https://fonts.googleapis.com"> + <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> + <link href="https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap" rel="stylesheet"> + + <title>{title}</title> + </head> + + <body class="bg-white"> + <Navbar /> + <slot /> + + </body> +</html> diff --git a/apps/website/src/pages/index.astro b/apps/website/src/pages/index.astro new file mode 100644 index 0000000..a484172 --- /dev/null +++ b/apps/website/src/pages/index.astro @@ -0,0 +1,10 @@ +--- +import Header from "@components/base/Header.astro"; +import Layout from "../layouts/Layout.astro"; +import Logo from "@components/logos/Logo.astro"; +--- + +<Layout> + <Header size="xl">test</Header> + <Header size="md">Test</Header> +</Layout> diff --git a/apps/website/src/styles/global.css b/apps/website/src/styles/global.css new file mode 100644 index 0000000..51f3c19 --- /dev/null +++ b/apps/website/src/styles/global.css @@ -0,0 +1,11 @@ +@tailwind base; + +* { + font-family: 'Poppins', sans-serif; +} + +@layer base { + :focus-visible { + @apply outline-none ring ring-blue-500; + } +}
\ No newline at end of file diff --git a/apps/website/src/types/Config.d.ts b/apps/website/src/types/Config.d.ts new file mode 100644 index 0000000..82eb4d7 --- /dev/null +++ b/apps/website/src/types/Config.d.ts @@ -0,0 +1,34 @@ +import { type configConst } from "config"; + +export type LogoType = typeof configConst.logos[number]; + +export interface Project { + name: string, + description: string, + logo?: LogoType, + tag?: string, +} + +export type NavbarDropdown = { + name: string, + description: string, + path: string, + logo?: LogoType, + tag?: string +} + +export type NavbarElement = { + text?: string, + logo?: [string, number], + path?: string, + dropdown?: NavbarDropdown[], +} + +export interface Config { + projects: Project[], + logos: string[], + navbar: { + left: NavbarElement[], + right: NavbarElement[], + }, +}
\ No newline at end of file |