diff --git a/frontend/src/app/(navfooter)/faq/page.tsx b/frontend/src/app/(navfooter)/faq/page.tsx index e0f86eea..efa783f9 100644 --- a/frontend/src/app/(navfooter)/faq/page.tsx +++ b/frontend/src/app/(navfooter)/faq/page.tsx @@ -2,7 +2,7 @@ import type { ReactNode } from "react"; import Link from "next/link"; -import Frog from "@/components/Nav/frog.svg"; +import Frog from "@/components/Frog/Frog"; const subtitle = "mt-8 text-xl font-semibold tracking-tight text-gray-11"; diff --git a/frontend/src/app/not-found.tsx b/frontend/src/app/not-found.tsx index af8c49e7..e50be234 100644 --- a/frontend/src/app/not-found.tsx +++ b/frontend/src/app/not-found.tsx @@ -1,7 +1,7 @@ import { ChevronRightIcon } from "@primer/octicons-react"; import GhostButton from "@/components/GhostButton"; -import Frog from "@/components/Nav/frog.svg"; +import Frog from "@/components/Frog/Frog"; export default function NotFound() { return ( diff --git a/frontend/src/components/Footer.tsx b/frontend/src/components/Footer.tsx index 8a4fe82f..cf62ad5a 100644 --- a/frontend/src/components/Footer.tsx +++ b/frontend/src/components/Footer.tsx @@ -4,7 +4,7 @@ import { MarkGithubIcon } from "@primer/octicons-react"; import DiscordIcon from "./discord.svg"; import GhostButton from "./GhostButton"; -import Logotype from "./Logotype"; +import SiteLogo from "./Nav/SiteLogo"; function Separator() { return
; @@ -20,7 +20,7 @@ export default function Footer() {
- +
diff --git a/frontend/src/components/user/AnonymousFrog.tsx b/frontend/src/components/Frog/AnonymousFrog.tsx similarity index 51% rename from frontend/src/components/user/AnonymousFrog.tsx rename to frontend/src/components/Frog/AnonymousFrog.tsx index af56cb23..b5143c69 100644 --- a/frontend/src/components/user/AnonymousFrog.tsx +++ b/frontend/src/components/Frog/AnonymousFrog.tsx @@ -1,12 +1,8 @@ import type { SVGProps } from "react"; - import clsx from "clsx"; - import type * as api from "@/lib/api"; -import Frog from "../Nav/frog.svg"; - -import styles from "./AnonymousFrog.module.scss"; +import Frog from "./Frog"; export type Props = SVGProps & { user: api.AnonymousUser; @@ -18,11 +14,11 @@ export default function AnonymousFrogAvatar({ className, ...props }: Props) { - const accentStyle = { - "--accent-hue": user.frog_color[0], - "--accent-saturation": user.frog_color[1], - "--accent-lightness": user.frog_color[2], - }; + const [hue, saturation, lightness] = user.frog_color; + + const primary = `hsl(${hue}, ${saturation * 100}%, ${lightness * 100}%)`; + const secondary = `hsl(${hue}, ${(0.3 * (1 - saturation) + saturation) * 100}%, ${(0.5 * (1 - lightness) + lightness) * 100}%)`; + const nose = `hsl(${hue}, ${(saturation - 0.2 * saturation) * 100}%, ${(lightness - 0.4 * lightness) * 100}%)`; return (
diff --git a/frontend/src/components/Frog/Frog.tsx b/frontend/src/components/Frog/Frog.tsx new file mode 100644 index 00000000..7a7872be --- /dev/null +++ b/frontend/src/components/Frog/Frog.tsx @@ -0,0 +1,31 @@ +import React from "react"; + +const Frog = ({ + primary = "#951fd9", + secondary = "#cc87f4", + pupil = "#000", + eye = "#FFF", + nose = "#505050", + title = "", + ...props +}) => ( + + {title} + + + + + + + + + +); + +export default Frog; diff --git a/frontend/src/components/Frog/HolidayFrog.tsx b/frontend/src/components/Frog/HolidayFrog.tsx new file mode 100644 index 00000000..b2408906 --- /dev/null +++ b/frontend/src/components/Frog/HolidayFrog.tsx @@ -0,0 +1,66 @@ +import React from "react"; +import Frog from "./Frog"; + +interface HolidayFrogProps { + today?: Date; + className?: string; +} + +export default function HolidayFrog({ + today = new Date(), + className = "size-7", +}: HolidayFrogProps) { + const month = today.getUTCMonth(); + const date = today.getUTCDate(); + + // TODO: special handling for frug + + // Defaults + let primary = "#951fd9"; + let secondary = "#cc87f4"; + let message = "Happy Decomping!"; + + if (month === 1 && date === 14) { + primary = "#e6396f"; + secondary = "#f7a1c4"; + message = "❤️ Happy Valentine's Day! ❤️"; + } + if (month === 2 && date === 17) { + primary = "#2a9d34"; + secondary = "#7fd28d"; + message = "🍀 Happy St Patrick's Day! 🍀"; + } + if (month === 9 && date === 31) { + primary = "#d96516"; + secondary = "#20150aff"; + message = "🎃 Happy Halloween! 🎃"; + } + + if (month === 10) { + // Thanksgiving (4th Thursday of November) + const firstDay = new Date(today.getFullYear(), 10, 1); + const firstThursday = 1 + ((4 - firstDay.getDay() + 7) % 7); + const fourthThursday = firstThursday + 21; + if (date === fourthThursday) { + primary = "#9e682a"; + secondary = "#d2a679"; + message = "🦃 Happy Thanksgiving! 🦃"; + } + } + + if (month === 11 && date === 25) { + primary = "#E61A1A"; + secondary = "#F8BFBF"; + message = "🎅 Merry Christmas! 🎅"; + } + + return ( + + ); +} diff --git a/frontend/src/components/Logotype.tsx b/frontend/src/components/Logotype.tsx deleted file mode 100644 index 33053da6..00000000 --- a/frontend/src/components/Logotype.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import Frog from "./Nav/frog.svg"; - -export default function Logotype() { - return ( -
- - - decomp.me - -
- ); -} diff --git a/frontend/src/components/Nav/Nav.module.scss b/frontend/src/components/Nav/Nav.module.scss index 4f6f807d..531314e0 100644 --- a/frontend/src/components/Nav/Nav.module.scss +++ b/frontend/src/components/Nav/Nav.module.scss @@ -64,47 +64,6 @@ $padding: 8px; width: 100%; height: 100%; } - - &:not(:hover) { - @keyframes blink { - 0% { - transform: scaleY(1); - opacity: 1; - } - - 25% { - transform: scaleY(0.2); - opacity: 0; - } - - 50% { - transform: scaleY(1); - opacity: 1; - } - - 75% { - transform: scaleY(0.2); - opacity: 0; - } - - 100% { - transform: scaleY(1); - opacity: 1; - } - } - - :global(.frog_svg__pupilR), - :global(.frog_svg__pupilL), - :global(.frog_svg__eyeR), - :global(.frog_svg__eyeL) { - transform-origin: 0 7px; - animation: blink 0.4s 2s ease; - - @media (prefers-reduced-motion) { - animation: none; - } - } - } } .menu { diff --git a/frontend/src/components/Nav/Nav.tsx b/frontend/src/components/Nav/Nav.tsx index 32a61c7f..d5fb5290 100644 --- a/frontend/src/components/Nav/Nav.tsx +++ b/frontend/src/components/Nav/Nav.tsx @@ -9,7 +9,7 @@ import { ThreeBarsIcon, XIcon } from "@primer/octicons-react"; import clsx from "clsx"; import GhostButton from "../GhostButton"; -import Logotype from "../Logotype"; +import SiteLogo from "./SiteLogo"; import LoginState from "./LoginState"; import styles from "./Nav.module.scss"; @@ -71,7 +71,7 @@ export default function Nav({ children }: Props) { href="/" className="transition-colors hover:text-gray-12 active:translate-y-px" > - +
  • @@ -108,7 +108,7 @@ export default function Nav({ children }: Props) {
    • - +
    • diff --git a/frontend/src/components/Nav/SiteLogo.module.scss b/frontend/src/components/Nav/SiteLogo.module.scss new file mode 100644 index 00000000..a357c306 --- /dev/null +++ b/frontend/src/components/Nav/SiteLogo.module.scss @@ -0,0 +1,39 @@ +@keyframes blink { + 0% { + transform: scaleY(1); + opacity: 1; + } + 25% { + transform: scaleY(0.2); + opacity: 0; + } + 50% { + transform: scaleY(1); + opacity: 1; + } + 75% { + transform: scaleY(0.2); + opacity: 0; + } + 100% { + transform: scaleY(1); + opacity: 1; + } +} + +.blinkingEyes :global(.pupilR), +.blinkingEyes :global(.pupilL), +.blinkingEyes :global(.eyeR), +.blinkingEyes :global(.eyeL) { + transform-origin: 0 7px; + animation: blink 0.4s 2s ease; +} + +@media (prefers-reduced-motion) { + .blinkingEyes :global(.pupilR), + .blinkingEyes :global(.pupilL), + .blinkingEyes :global(.eyeR), + .blinkingEyes :global(.eyeL) { + animation: none; + } +} diff --git a/frontend/src/components/Nav/SiteLogo.tsx b/frontend/src/components/Nav/SiteLogo.tsx new file mode 100644 index 00000000..2aeea984 --- /dev/null +++ b/frontend/src/components/Nav/SiteLogo.tsx @@ -0,0 +1,20 @@ +import clsx from "clsx"; +import HolidayFrog from "../Frog/HolidayFrog"; +import styles from "./SiteLogo.module.scss"; + +export default function SiteLogo() { + return ( +
      + + + decomp.me + +
      + ); +} diff --git a/frontend/src/components/Nav/frog.svg b/frontend/src/components/Nav/frog.svg deleted file mode 100644 index 3ef8da6b..00000000 --- a/frontend/src/components/Nav/frog.svg +++ /dev/null @@ -1,11 +0,0 @@ - - decomp.me - - - - - - - - - diff --git a/frontend/src/components/ScratchItem.tsx b/frontend/src/components/ScratchItem.tsx index 02d66518..70125c39 100644 --- a/frontend/src/components/ScratchItem.tsx +++ b/frontend/src/components/ScratchItem.tsx @@ -13,7 +13,7 @@ import { presetUrl, scratchUrl, userAvatarUrl } from "@/lib/api/urls"; import getTranslation from "@/lib/i18n/translate"; -import AnonymousFrogAvatar from "./user/AnonymousFrog"; +import AnonymousFrogAvatar from "./Frog/AnonymousFrog"; import PlatformLink from "./PlatformLink"; import { calculateScorePercent, percentToString } from "./ScoreBadge"; import styles from "./ScratchItem.module.scss"; diff --git a/frontend/src/components/user/AnonymousFrog.module.scss b/frontend/src/components/user/AnonymousFrog.module.scss deleted file mode 100644 index f39e5789..00000000 --- a/frontend/src/components/user/AnonymousFrog.module.scss +++ /dev/null @@ -1,27 +0,0 @@ -.anonymousFrog { - /* Default: decomp.me purple */ - --accent-hue: 298; - --accent-saturation: 0.75; - --accent-lightness: 0.4862; - --frog-pupil: #292f33; - --frog-primary: - hsl( - var(--accent-hue), - calc(100% * var(--accent-saturation)), - calc(100% * var(--accent-lightness)) - ); - - /* Pure CSS doesn't have anything quite as slick as Sass' color.scale [as used in theme.scss] - for creating variations of a color. As such, we have to do a bit of the maths ourselves. */ - --frog-secondary: - hsl( - var(--accent-hue), - calc(100% * (0.3 * (1 - var(--accent-saturation)) + var(--accent-saturation))), /* 30% towards maximum saturation */ - calc(100% * (0.5 * (1 - var(--accent-lightness)) + var(--accent-lightness))) /* 50% towards maximum lightness */ - ); - --frog-nose: - hsl( - calc(100% * (-0.2 * (var(--accent-saturation)) + var(--accent-saturation))), /* 20% towards minimum saturation */ - calc(100% * (-0.4 * (var(--accent-lightness)) + var(--accent-lightness))) /* 40% towards minimum lightness */ - ); -} diff --git a/frontend/src/components/user/UserAvatar.tsx b/frontend/src/components/user/UserAvatar.tsx index 7dd7d3fe..0d8da4ba 100644 --- a/frontend/src/components/user/UserAvatar.tsx +++ b/frontend/src/components/user/UserAvatar.tsx @@ -9,7 +9,7 @@ import clsx from "clsx"; import * as api from "@/lib/api"; import { userAvatarUrl } from "@/lib/api/urls"; -import AnonymousFrogAvatar from "./AnonymousFrog"; +import AnonymousFrogAvatar from "../Frog/AnonymousFrog"; import styles from "./UserAvatar.module.scss"; export type Props = {