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
+}) => (
+
+);
+
+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 @@
-
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 = {