From f1e891413acf1206b72b23e313b39192a49855cd Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Sun, 7 Sep 2025 00:16:51 +0200 Subject: [PATCH] feat: enhance user interface with improved styling and responsiveness - Updated UserAvatar component to accept additional className for better customization. - Refined ProfilePage layout with responsive avatar styling. - Enhanced Header component with improved background and text styles. - Improved PopularTags and TopFriends components with better spacing and text shadows. - Updated ThoughtCard and ThoughtThread components for better visual hierarchy and responsiveness. - Enhanced UI components (Button, Badge, Card, DropdownMenu, Input, Popover, Separator, Skeleton, Textarea) with new styles and effects. - Added a new background image for visual enhancement. --- thoughts-frontend/app/globals.css | 213 +++++++++++++++--- .../app/users/[username]/page.tsx | 6 +- thoughts-frontend/components/header.tsx | 10 +- thoughts-frontend/components/popular-tags.tsx | 13 +- thoughts-frontend/components/thought-card.tsx | 28 ++- .../components/thought-thread.tsx | 2 +- thoughts-frontend/components/top-friends.tsx | 22 +- thoughts-frontend/components/ui/badge.tsx | 27 ++- thoughts-frontend/components/ui/button.tsx | 45 ++-- thoughts-frontend/components/ui/card.tsx | 27 ++- .../components/ui/dropdown-menu.tsx | 53 ++--- thoughts-frontend/components/ui/input.tsx | 10 +- thoughts-frontend/components/ui/popover.tsx | 21 +- thoughts-frontend/components/ui/separator.tsx | 14 +- thoughts-frontend/components/ui/skeleton.tsx | 8 +- thoughts-frontend/components/ui/textarea.tsx | 9 +- thoughts-frontend/components/user-avatar.tsx | 14 +- thoughts-frontend/public/background.jpeg | Bin 0 -> 1366235 bytes 18 files changed, 348 insertions(+), 174 deletions(-) create mode 100644 thoughts-frontend/public/background.jpeg diff --git a/thoughts-frontend/app/globals.css b/thoughts-frontend/app/globals.css index dc98be7..8fa309e 100644 --- a/thoughts-frontend/app/globals.css +++ b/thoughts-frontend/app/globals.css @@ -41,28 +41,28 @@ --radius-md: calc(var(--radius) - 2px); --radius-lg: var(--radius); --radius-xl: calc(var(--radius) + 4px); + + /* Frutiger Aero Gradients */ + --gradient-fa-blue: 135deg, hsl(217 91% 60%) 0%, hsl(200 90% 70%) 100%; + --gradient-fa-green: 135deg, hsl(155 70% 55%) 0%, hsl(170 80% 65%) 100%; + --gradient-fa-card: 180deg, hsl(var(--card)) 0%, hsl(var(--card)) 90%, + hsl(var(--card)) 100%; + --gradient-fa-gloss: 135deg, rgba(255, 255, 255, 0.2) 0%, + rgba(255, 255, 255, 0) 100%; + + --shadow-fa-sm: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06); + --shadow-fa-md: 0 4px 6px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.06); + --shadow-fa-lg: 0 10px 15px rgba(0, 0, 0, 0.1), 0 4px 6px rgba(0, 0, 0, 0.05); + --fa-inner: inset 0 1px 2px rgba(0, 0, 0, 0.1); + + --text-shadow-default: 0 1px 1px rgba(0, 0, 0, 0.2); + --text-shadow-sm: 0 1px 0px rgba(255, 255, 255, 0.4); + --text-shadow-md: 0 2px 2px rgba(0, 0, 0, 0.2); + --text-shadow-lg: 0 4px 4px rgba(0, 0, 0, 0.2); } :root { - --radius: 0.625rem; - --background: oklch(1 0 0); - --foreground: oklch(0.145 0 0); - --card: oklch(1 0 0); - --card-foreground: oklch(0.145 0 0); - --popover: oklch(1 0 0); - --popover-foreground: oklch(0.145 0 0); - --primary: oklch(0.205 0 0); - --primary-foreground: oklch(0.985 0 0); - --secondary: oklch(0.97 0 0); - --secondary-foreground: oklch(0.205 0 0); - --muted: oklch(0.97 0 0); - --muted-foreground: oklch(0.556 0 0); - --accent: oklch(0.97 0 0); - --accent-foreground: oklch(0.205 0 0); --destructive: oklch(0.577 0.245 27.325); - --border: oklch(0.922 0 0); - --input: oklch(0.922 0 0); - --ring: oklch(0.708 0 0); --chart-1: oklch(0.646 0.222 41.116); --chart-2: oklch(0.6 0.118 184.704); --chart-3: oklch(0.398 0.07 227.392); @@ -76,27 +76,40 @@ --sidebar-accent-foreground: oklch(0.205 0 0); --sidebar-border: oklch(0.922 0 0); --sidebar-ring: oklch(0.708 0 0); + + --background: hsl(0 0% 98%); /* Light off-white */ + --foreground: hsl(222.2 47.4% 11.2%); + + --muted: hsl(210 20% 96.1%); + --muted-foreground: hsl(215.4 16.3% 46.9%); + + --popover: hsl(0 0% 100%); + --popover-foreground: hsl(222.2 47.4% 11.2%); + + --card: hsl(0 0% 100%); /* Pure white for a crisp look */ + --card-foreground: hsl(222.2 47.4% 11.2%); + + --border: hsl(214.3 31.8% 91.4%); + --input: hsl(214.3 31.8% 91.4%); + --ring: hsl(222.2 47.4% 11.2%); + + --primary: hsl(217 91% 60%); /* Vibrant Blue */ + --primary-foreground: hsl(210 40% 98%); + + --secondary: hsl(155 70% 55%); /* Vibrant Green */ + --secondary-foreground: hsl(210 40% 98%); + + --destructive: hsl(0 84.2% 60.2%); + --destructive-foreground: hsl(210 40% 98%); + + --accent: hsl(210 20% 96.1%); + --accent-foreground: hsl(222.2 47.4% 11.2%); + + --radius: 0.75rem; /* Larger border radius */ } .dark { - --background: oklch(0.145 0 0); - --foreground: oklch(0.985 0 0); - --card: oklch(0.205 0 0); - --card-foreground: oklch(0.985 0 0); - --popover: oklch(0.205 0 0); - --popover-foreground: oklch(0.985 0 0); - --primary: oklch(0.922 0 0); - --primary-foreground: oklch(0.205 0 0); - --secondary: oklch(0.269 0 0); - --secondary-foreground: oklch(0.985 0 0); - --muted: oklch(0.269 0 0); - --muted-foreground: oklch(0.708 0 0); - --accent: oklch(0.269 0 0); - --accent-foreground: oklch(0.985 0 0); --destructive: oklch(0.704 0.191 22.216); - --border: oklch(1 0 0 / 10%); - --input: oklch(1 0 0 / 15%); - --ring: oklch(0.556 0 0); --chart-1: oklch(0.488 0.243 264.376); --chart-2: oklch(0.696 0.17 162.48); --chart-3: oklch(0.769 0.188 70.08); @@ -110,13 +123,143 @@ --sidebar-accent-foreground: oklch(0.985 0 0); --sidebar-border: oklch(1 0 0 / 10%); --sidebar-ring: oklch(0.556 0 0); + + --background: hsl(222.2 47.4% 11.2%); + --foreground: hsl(210 40% 98%); + + --muted: hsl(217.2 32.4% 14.8%); + --muted-foreground: hsl(215 20.2% 65.1%); + + --popover: hsl(222.2 47.4% 11.2%); + --popover-foreground: hsl(210 40% 98%); + + --card: hsl(217.2 32.4% 14.8%); + --card-foreground: hsl(210 40% 98%); + + --border: hsl(217.2 32.4% 14.8%); + --input: hsl(217.2 32.4% 14.8%); + --ring: hsl(212.7 26.8% 83.9%); + + --primary: hsl(217 91% 60%); /* Vibrant Blue (same as light) */ + --primary-foreground: hsl(210 40% 98%); + + --secondary: hsl(155 70% 55%); /* Vibrant Green (same as light) */ + --secondary-foreground: hsl(210 40% 98%); + + --destructive: hsl(0 62.8% 30.6%); + --destructive-foreground: hsl(210 40% 98%); + + --accent: hsl(217.2 32.4% 14.8%); + --accent-foreground: hsl(210 40% 98%); + + /* Frutiger Aero Gradients for dark mode (slightly adjusted) */ + --color-fa-gradient-blue: linear-gradient( + 135deg, + hsl(217 91% 45%) 0%, + hsl(200 90% 55%) 100% + ); + --color-fa-gradient-green: linear-gradient( + 135deg, + hsl(155 70% 40%) 0%, + hsl(170 80% 50%) 100% + ); + --color-fa-gradient-card: linear-gradient( + 180deg, + hsl(var(--card)) 0%, + hsl(var(--card)) 90%, + hsl(var(--card)) 100% + ); } @layer base { * { @apply border-border outline-ring/50; } + body { @apply bg-background text-foreground; + background: linear-gradient( + 135deg, + hsl(var(--background)) 0%, + hsl(var(--background)) 70%, + hsl(var(--primary) / 0.1) 100% + ); + } + + .glossy-effect::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 50%; + border-radius: var(--radius); /* Inherit parent's border radius */ + background: linear-gradient( + 180deg, + rgba(255, 255, 255, 0.4) 0%, + rgba(255, 255, 255, 0.1) 100% + ); + opacity: 0.8; + pointer-events: none; /* Allow clicks to pass through */ + z-index: 1; /* Ensure it's above the background but below content */ + } + + .glossy-effect.bottom::after { + content: ""; + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 30%; + border-radius: var(--radius); + background: linear-gradient( + 0deg, + rgba(0, 0, 0, 0.1) 0%, + rgba(0, 0, 0, 0) 100% + ); + pointer-events: none; + z-index: 1; + } + + .fa-gradient-blue { + background: linear-gradient(var(--gradient-fa-blue)); + } + .fa-gradient-green { + background: linear-gradient(var(--gradient-fa-green)); + } + .fa-gradient-card { + background: linear-gradient(var(--gradient-fa-card)); + } + .fa-gloss { + position: relative; + } + .fa-gloss::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 50%; + border-radius: var(--radius); + background: linear-gradient(var(--gradient-fa-gloss)); + opacity: 0.8; + pointer-events: none; + z-index: 1; + } + .fa-gloss.bottom::after { + content: ""; + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 30%; + border-radius: var(--radius); + background: linear-gradient( + 0deg, + rgba(0, 0, 0, 0.1) 0%, + rgba(0, 0, 0, 0) 100% + ); + pointer-events: none; + z-index: 1; } } diff --git a/thoughts-frontend/app/users/[username]/page.tsx b/thoughts-frontend/app/users/[username]/page.tsx index d9fdf91..4c51630 100644 --- a/thoughts-frontend/app/users/[username]/page.tsx +++ b/thoughts-frontend/app/users/[username]/page.tsx @@ -96,7 +96,11 @@ export default async function ProfilePage({ params }: ProfilePageProps) {
- +
{/* Action Button */} diff --git a/thoughts-frontend/components/header.tsx b/thoughts-frontend/components/header.tsx index fd7cf24..507ecc8 100644 --- a/thoughts-frontend/components/header.tsx +++ b/thoughts-frontend/components/header.tsx @@ -5,22 +5,22 @@ import Link from "next/link"; import { Button } from "./ui/button"; import { UserNav } from "./user-nav"; import { MainNav } from "./main-nav"; -import { ThemeToggle } from "./theme-toggle"; export function Header() { const { token } = useAuth(); return ( -
-
+
+
- Thoughts + + Thoughts +
- {token ? ( ) : ( diff --git a/thoughts-frontend/components/popular-tags.tsx b/thoughts-frontend/components/popular-tags.tsx index 9464f68..83fed69 100644 --- a/thoughts-frontend/components/popular-tags.tsx +++ b/thoughts-frontend/components/popular-tags.tsx @@ -23,22 +23,25 @@ export async function PopularTags() { } return ( - - - Popular Tags + + + Popular Tags - + {tags.map((tag) => ( {tag} ))} + {tags.length === 0 && ( +

No popular tags yet.

+ )}
); diff --git a/thoughts-frontend/components/thought-card.tsx b/thoughts-frontend/components/thought-card.tsx index 2ee86a0..ab7cfcb 100644 --- a/thoughts-frontend/components/thought-card.tsx +++ b/thoughts-frontend/components/thought-card.tsx @@ -38,6 +38,7 @@ import { } from "lucide-react"; import { ReplyForm } from "@/components/reply-form"; import Link from "next/link"; +import { cn } from "@/lib/utils"; interface ThoughtCardProps { thought: Thought; @@ -83,16 +84,19 @@ export function ThoughtCard({ <>
{thought.replyToId && isReply && ( -
- +
+ Replying to{" "} parent thought @@ -100,16 +104,18 @@ export function ThoughtCard({
)}
- +
{author.username} - {timeAgo} + + {timeAgo} +
@@ -138,11 +144,13 @@ export function ThoughtCard({
-

{thought.content}

+

+ {thought.content} +

{token && ( - +