diff --git a/package.json b/package.json index 4378d36..0ccd5e6 100644 --- a/package.json +++ b/package.json @@ -24,12 +24,12 @@ "clsx": "^2.1.1", "cmdk": "1.1.1", "geist": "^1.5.1", - "lucide-react": "^0.545.0", "next": "^15.5.14", "next-themes": "^0.4.6", "react": "^19.2.3", "react-dom": "^19.2.3", "react-hook-form": "^7.68.0", + "react-icons": "^5.6.0", "tailwind-merge": "^3.4.0", "tailwindcss-animate": "^1.0.7", "zod": "^4.2.1" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e1c5681..2c9e7c1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -38,9 +38,6 @@ importers: geist: specifier: ^1.5.1 version: 1.5.1(next@15.5.14(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)) - lucide-react: - specifier: ^0.545.0 - version: 0.545.0(react@19.2.3) next: specifier: ^15.5.14 version: 15.5.14(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -56,6 +53,9 @@ importers: react-hook-form: specifier: ^7.68.0 version: 7.68.0(react@19.2.3) + react-icons: + specifier: ^5.6.0 + version: 5.6.0(react@19.2.3) tailwind-merge: specifier: ^3.4.0 version: 3.4.0 @@ -1005,11 +1005,6 @@ packages: resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==} engines: {node: '>= 12.0.0'} - lucide-react@0.545.0: - resolution: {integrity: sha512-7r1/yUuflQDSt4f1bpn5ZAocyIxcTyVyBBChSVtBKn5M+392cPmI5YJMWOJKk/HUWGm5wg83chlAZtCcGbEZtw==} - peerDependencies: - react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 - magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} @@ -1067,6 +1062,11 @@ packages: peerDependencies: react: ^16.8.0 || ^17 || ^18 || ^19 + react-icons@5.6.0: + resolution: {integrity: sha512-RH93p5ki6LfOiIt0UtDyNg/cee+HLVR6cHHtW3wALfo+eOHTp8RnU2kRkI6E+H19zMIs03DyxUG/GfZMOGvmiA==} + peerDependencies: + react: '*' + react-remove-scroll-bar@2.3.8: resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} engines: {node: '>=10'} @@ -1874,10 +1874,6 @@ snapshots: lightningcss-win32-arm64-msvc: 1.30.2 lightningcss-win32-x64-msvc: 1.30.2 - lucide-react@0.545.0(react@19.2.3): - dependencies: - react: 19.2.3 - magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -1936,6 +1932,10 @@ snapshots: dependencies: react: 19.2.3 + react-icons@5.6.0(react@19.2.3): + dependencies: + react: 19.2.3 + react-remove-scroll-bar@2.3.8(@types/react@19.2.7)(react@19.2.3): dependencies: react: 19.2.3 diff --git a/src/app/page.tsx b/src/app/page.tsx index 7422510..62c7213 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,5 +1,61 @@ +import { FiBook, FiBookOpen, FiClipboard, FiFileText, FiPenTool, FiTriangle, FiUploadCloud } from "react-icons/fi" +import { CardIcon } from "@/components/card-icon" import { Hero } from "@/components/home/hero" +const schoolCards = [ + { title: "Scuola di Architettura", icon: FiTriangle, size: "md" }, + { title: "Scuola di Design", icon: FiPenTool, size: "md" }, + { title: "Scuola di Ingegneria", icon: FiBookOpen, size: "md" }, +] as const + +const materialCards = [ + { + title: "Carica", + description: + "Hai appunti, dispense o temi d'esame che vuoi condividere? Caricali qui! Il tuo contributo è prezioso per aiutare migliaia di colleghi con materiale aggiornato!", + icon: FiUploadCloud, + size: "lg", + }, + { + title: "Visualizza", + description: + "Cerca ciò che ti serve per il tuo prossimo esame. Naviga tra i corsi di studio e trova facilmente appunti, esercizi e dispense condivisi da altri studenti come te.", + icon: FiBookOpen, + size: "lg", + }, +] as const + +const otherCards = [ + { title: "Dispense", icon: FiBook, size: "sm" }, + { title: "Appunti", icon: FiFileText, size: "sm" }, + { title: "Esami", icon: FiClipboard, size: "sm" }, +] as const + export default function Home() { - return + return ( +
+ +
+
+
+ {schoolCards.map((card) => ( + + ))} +
+
+
+
+ {materialCards.map((card) => ( + + ))} +
+
+ {otherCards.map((card) => ( + + ))} +
+
+
+
+ ) } diff --git a/src/components/card-icon/basic-card-media.tsx b/src/components/card-icon/basic-card-media.tsx new file mode 100644 index 0000000..c037bfd --- /dev/null +++ b/src/components/card-icon/basic-card-media.tsx @@ -0,0 +1,7 @@ +import { GradientIcon, type GradientIconType } from "../gradient-icon" +import type { CardSize } from "./types" +import { getIconSizeClasses } from "./utils" + +export function BasicCardMedia({ icon: Icon, size }: { icon: GradientIconType; size: CardSize }) { + return +} diff --git a/src/components/card-icon/description-card-media.tsx b/src/components/card-icon/description-card-media.tsx new file mode 100644 index 0000000..668dcff --- /dev/null +++ b/src/components/card-icon/description-card-media.tsx @@ -0,0 +1,12 @@ +import { cn } from "@/lib/utils" +import { GradientIcon, type GradientIconType } from "../gradient-icon" +import type { CardSize } from "./types" +import { getIconSizeClasses } from "./utils" + +export function DescriptionCardMedia({ icon: Icon, size }: { icon: GradientIconType; size: CardSize }) { + return ( +
+ +
+ ) +} diff --git a/src/components/card-icon/hover-background.tsx b/src/components/card-icon/hover-background.tsx new file mode 100644 index 0000000..8272645 --- /dev/null +++ b/src/components/card-icon/hover-background.tsx @@ -0,0 +1,15 @@ +import { Shape } from "@/components/shapes" + +export function CardHoverBackground() { + return ( +
+ + +
+
+ +
+
+
+ ) +} diff --git a/src/components/card-icon/index.tsx b/src/components/card-icon/index.tsx new file mode 100644 index 0000000..f0c0a8d --- /dev/null +++ b/src/components/card-icon/index.tsx @@ -0,0 +1,61 @@ +import { Glass } from "@/components/glass" +import { cn } from "@/lib/utils" +import { BasicCardMedia } from "./basic-card-media" +import { DescriptionCardMedia } from "./description-card-media" +import { CardHoverBackground } from "./hover-background" +import type { CardIconProps } from "./types" +import { getCardPaddingClasses, getContentGapClasses } from "./utils" + +export function CardIcon(props: CardIconProps) { + const { title, icon, size = "md", href, hoverEffect = false, className } = props + const description = "description" in props ? props.description : undefined + const Root = href ? "a" : "div" + const isDescriptionCard = Boolean(description) + + return ( + + + {hoverEffect && } + +
+
+ {isDescriptionCard ? ( + + ) : ( + + )} +
+ +
+

+ {title} +

+ {description &&

{description}

} +
+
+
+
+ ) +} diff --git a/src/components/card-icon/types.ts b/src/components/card-icon/types.ts new file mode 100644 index 0000000..4f02808 --- /dev/null +++ b/src/components/card-icon/types.ts @@ -0,0 +1,18 @@ +import type { GradientIconType } from "@/components/gradient-icon" + +export type CardSize = "sm" | "md" | "lg" + +export type SharedCardProps = { + title: string + icon: GradientIconType + size?: CardSize + href?: string + hoverEffect?: boolean + className?: string +} + +export type CardWithDescriptionProps = SharedCardProps & { + description: string +} + +export type CardIconProps = SharedCardProps | CardWithDescriptionProps diff --git a/src/components/card-icon/utils.ts b/src/components/card-icon/utils.ts new file mode 100644 index 0000000..b9f0a95 --- /dev/null +++ b/src/components/card-icon/utils.ts @@ -0,0 +1,17 @@ +import type { CardSize } from "./types" + +export function getIconSizeClasses(size: CardSize) { + if (size === "sm") return "h-14 w-14" + if (size === "lg") return "h-44 w-44" + return "h-32 w-32" +} + +export function getCardPaddingClasses(size: CardSize, hasDescription: boolean) { + if (!hasDescription && size === "sm") return "px-8 py-4" + return "p-8" +} + +export function getContentGapClasses(size: CardSize) { + if (size === "sm") return "gap-2" + return "gap-6" +} diff --git a/src/components/gradient-icon.tsx b/src/components/gradient-icon.tsx new file mode 100644 index 0000000..0b49c7f --- /dev/null +++ b/src/components/gradient-icon.tsx @@ -0,0 +1,29 @@ +import { type FunctionComponent, type SVGProps, useId } from "react" + +export type GradientIconType = FunctionComponent> + +type GradientIconProps = { + icon: GradientIconType + className: string +} + +export function GradientIcon({ icon: Icon, className }: GradientIconProps) { + const iconId = useId().replaceAll(":", "") + const gradientId = `icon-gradient-${iconId}` + const maskId = `icon-mask-${iconId}` + + return ( + + ) +} diff --git a/src/components/header.tsx b/src/components/header.tsx index 64b77a5..453dc7a 100644 --- a/src/components/header.tsx +++ b/src/components/header.tsx @@ -1,6 +1,6 @@ -import { GlobeIcon } from "lucide-react" import Image from "next/image" import Link from "next/link" +import { FiGlobe } from "react-icons/fi" import { ThemeButton } from "@/components/theme-button" export const HEADER_HEIGHT = "4.5rem" @@ -28,7 +28,7 @@ export async function Header() { diff --git a/src/components/home/hero.tsx b/src/components/home/hero.tsx index d4797ef..a3af5db 100644 --- a/src/components/home/hero.tsx +++ b/src/components/home/hero.tsx @@ -1,4 +1,4 @@ -import { Search, Send, UserPlus } from "lucide-react" +import { FiNavigation, FiSearch, FiUserPlus } from "react-icons/fi" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" @@ -15,7 +15,7 @@ export function Hero() {
} + icon={} type="text" placeholder="Find your group" aria-label="Find your group" @@ -23,16 +23,16 @@ export function Hero() { className="typo-body-medium" /> -
diff --git a/src/components/theme-button.tsx b/src/components/theme-button.tsx index 7a4036e..b5fe26f 100644 --- a/src/components/theme-button.tsx +++ b/src/components/theme-button.tsx @@ -1,14 +1,14 @@ "use client" -import { MoonIcon, SunIcon } from "lucide-react" import { useTheme } from "next-themes" +import { FiMoon, FiSun } from "react-icons/fi" export function ThemeButton() { const { resolvedTheme, setTheme } = useTheme() return ( // TODO: enable when dark mode design is ready ) } diff --git a/src/components/ui/buttonWithIcon.tsx b/src/components/ui/buttonWithIcon.tsx index 7b83aea..3e5fecd 100644 --- a/src/components/ui/buttonWithIcon.tsx +++ b/src/components/ui/buttonWithIcon.tsx @@ -1,4 +1,4 @@ -import type { LucideIcon } from "lucide-react" +import type { IconType } from "react-icons" import { Button } from "./button" export function ButtonWithIcon({ @@ -8,7 +8,7 @@ export function ButtonWithIcon({ iconPosition = "left", }: { variant?: "primary" | "tertiary" | "tertiaryBlur" - icon: LucideIcon + icon: IconType text: string iconPosition?: "left" | "right" }) { diff --git a/src/components/ui/select.tsx b/src/components/ui/select.tsx index 197e867..61c8927 100644 --- a/src/components/ui/select.tsx +++ b/src/components/ui/select.tsx @@ -1,6 +1,6 @@ import * as SelectPrimitive from "@radix-ui/react-select" -import { ChevronDownIcon, ChevronUpIcon } from "lucide-react" import type * as React from "react" +import { FiChevronDown, FiChevronUp } from "react-icons/fi" import { cn } from "@/lib/utils" import { Glass } from "../glass" @@ -16,7 +16,7 @@ function SelectValue({ className, ...props }: React.ComponentProps ) @@ -47,7 +47,7 @@ function SelectTrigger({ > {children} - + @@ -74,7 +74,7 @@ function SelectContent({ "data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2", "data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2", - "relative z-50 max-h-[var(--radix-select-content-available-height)]", + "relative z-50 max-h-(--radix-select-content-available-height)", "origin-(--radix-select-content-transform-origin) overflow-y-auto overflow-x-hidden", "rounded-rectangles border border-white/40", "bg-background-blur shadow-lg backdrop-blur-xl", @@ -90,8 +90,7 @@ function SelectContent({ className={cn( // il menu matcha la larghezza del trigger "p-1", - position === "popper" && - "h-[var(--radix-select-trigger-height)] w-[var(--radix-select-trigger-width)] scroll-my-1" + position === "popper" && "h-(--radix-select-trigger-height) w-(--radix-select-trigger-width) scroll-my-1" )} > {children} @@ -121,8 +120,8 @@ function SelectItem({ className, children, ...props }: React.ComponentProps - + ) } @@ -164,7 +163,7 @@ function SelectScrollDownButton({ className={cn("flex cursor-default items-center justify-center py-1", className)} {...props} > - + ) }