feat: add Frutiger font, enhance UI with glass effect and shadows, and improve component styling

This commit is contained in:
2025-09-07 01:12:09 +02:00
parent f1e891413a
commit c3539cfc11
16 changed files with 173 additions and 76 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -42,6 +42,9 @@
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--background-start: var(--color-sky-blue);
--background-end: var(--color-lime-300);
/* 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%;
@@ -59,6 +62,8 @@
--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);
--font-display: var(--font-frutiger), "Arial", "Helvetica", "sans-serif";
}
:root {
@@ -178,12 +183,11 @@
body {
@apply bg-background text-foreground;
background: linear-gradient(
135deg,
hsl(var(--background)) 0%,
hsl(var(--background)) 70%,
hsl(var(--primary) / 0.1) 100%
);
background-image: url("/background.jpeg");
background-size: cover;
background-position: center;
background-attachment: fixed;
background-repeat: no-repeat;
}
.glossy-effect::before {
@@ -263,3 +267,48 @@
z-index: 1;
}
}
@layer components {
.shadow-fa-sm {
box-shadow: var(--shadow-fa-sm), var(--fa-inner);
}
.shadow-fa-md {
box-shadow: var(--shadow-fa-md), var(--fa-inner);
}
.shadow-fa-lg {
box-shadow: var(--shadow-fa-lg), var(--fa-inner);
}
.text-shadow-default {
text-shadow: var(--text-shadow-default);
}
.text-shadow-sm {
text-shadow: var(--text-shadow-sm);
}
.text-shadow-md {
text-shadow: var(--text-shadow-md);
}
.text-shadow-lg {
text-shadow: var(--text-shadow-lg);
}
.glass-effect {
@apply bg-card/70 backdrop-blur-lg border border-white/20 shadow-fa-lg;
}
.gloss-highlight::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
height: 60%;
border-radius: inherit; /* This is key for matching the parent's border radius */
background: linear-gradient(
180deg,
rgba(255, 255, 255, 0.5) 0%,
rgba(255, 255, 255, 0) 100%
);
pointer-events: none;
z-index: 1;
}
}

View File

@@ -4,22 +4,29 @@ import "./globals.css";
import { AuthProvider } from "@/hooks/use-auth";
import { Toaster } from "@/components/ui/sonner";
import { Header } from "@/components/header";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
import localFont from "next/font/local";
export const metadata: Metadata = {
title: "Thoughts",
description: "A social network for sharing thoughts",
};
const frutiger = localFont({
src: [
{
path: "./frutiger.woff",
weight: "normal",
style: "normal",
},
{
path: "./frutiger-bold.woff",
weight: "bold",
style: "normal",
},
],
variable: "--font-frutiger",
});
export default function RootLayout({
children,
}: Readonly<{
@@ -27,9 +34,7 @@ export default function RootLayout({
}>) {
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
<body className={`${frutiger.className} antialiased`}>
<AuthProvider>
<Header />
<main className="flex-1">{children}</main>

View File

@@ -51,7 +51,7 @@ async function FeedPage({ token }: { token: string }) {
<main className="col-span-1 lg:col-span-2 space-y-6">
<header className="mb-6">
<h1 className="text-3xl font-bold">Your Feed</h1>
<h1 className="text-3xl font-bold text-shadow-sm">Your Feed</h1>
</header>
<PostThoughtForm />
<div className="space-y-6">

View File

@@ -76,26 +76,36 @@ export default async function ProfilePage({ params }: ProfilePageProps) {
authorDetails.set(user.username, { avatarUrl: user.avatarUrl });
return (
<div>
<div id={`profile-page-${user.username}`}>
{user.customCss && (
<style dangerouslySetInnerHTML={{ __html: user.customCss }} />
)}
<div
id="profile-header"
className="h-48 bg-gray-200 bg-cover bg-center profile-header"
style={{
backgroundImage: user.headerUrl ? `url(${user.headerUrl})` : "none",
}}
/>
<main className="container mx-auto max-w-6xl p-4 -mt-16 grid grid-cols-1 lg:grid-cols-4 gap-8">
<main
id="main-container"
className="container mx-auto max-w-6xl p-4 -mt-16 grid grid-cols-1 lg:grid-cols-4 gap-8"
>
{/* Left Sidebar (Profile Card & Top Friends) */}
<aside className="col-span-1 lg:col-span-1 space-y-6">
<div className="sticky top-20 space-y-6">
<Card className="p-6 bg-card/80 backdrop-blur-lg">
<div className="flex justify-between items-start">
<div className="flex items-end gap-4">
<div className="w-24 h-24 rounded-full border-4 border-background shrink-0">
<aside id="left-sidebar" className="col-span-1 lg:col-span-1 space-y-6">
<div id="left-sidebar__inner" className="sticky top-20 space-y-6">
<Card id="profile-card" className="p-6 bg-card/80 backdrop-blur-lg">
<div
id="profile-card__inner"
className="flex justify-between items-start"
>
<div id="profile-card__avatar" className="flex items-end gap-4">
<div
id="profile-card__avatar-image"
className="w-24 h-24 rounded-full border-4 border-background shrink-0"
>
<UserAvatar
src={user.avatarUrl}
alt={user.displayName}
@@ -104,7 +114,7 @@ export default async function ProfilePage({ params }: ProfilePageProps) {
</div>
</div>
{/* Action Button */}
<div>
<div id="profile-card__action">
{isOwnProfile ? (
<Button asChild variant="outline" size="sm">
<Link href="/settings/profile">
@@ -120,19 +130,30 @@ export default async function ProfilePage({ params }: ProfilePageProps) {
</div>
</div>
<div className="mt-4">
<h1 className="text-2xl font-bold">
<div id="profile-card__info" className="mt-4">
<h1 id="profile-card__name" className="text-2xl font-bold">
{user.displayName || user.username}
</h1>
<p className="text-sm text-muted-foreground">
<p
id="profile-card__username"
className="text-sm text-muted-foreground"
>
@{user.username}
</p>
</div>
<p className="mt-4 text-sm whitespace-pre-wrap">{user.bio}</p>
<p
id="profile-card__bio"
className="mt-4 text-sm whitespace-pre-wrap"
>
{user.bio}
</p>
{isOwnProfile && (
<div className="flex items-center gap-4 mt-4 text-sm">
<div
id="profile-card__stats"
className="flex items-center gap-4 mt-4 text-sm"
>
<Link
href={`/users/${user.username}/following`}
className="hover:underline"
@@ -154,7 +175,10 @@ export default async function ProfilePage({ params }: ProfilePageProps) {
</div>
)}
<div className="flex items-center gap-2 mt-4 text-sm text-muted-foreground">
<div
id="profile-card__joined"
className="flex items-center gap-2 mt-4 text-sm text-muted-foreground"
>
<Calendar className="h-4 w-4" />
<span>
Joined {new Date(user.joinedAt).toLocaleDateString()}
@@ -166,7 +190,10 @@ export default async function ProfilePage({ params }: ProfilePageProps) {
</div>
</aside>
<div className="col-span-1 lg:col-span-3 space-y-4">
<div
id="profile-card__thoughts"
className="col-span-1 lg:col-span-3 space-y-4"
>
{topLevelThoughts.map((thought) => (
<ThoughtThread
key={thought.id}
@@ -177,7 +204,10 @@ export default async function ProfilePage({ params }: ProfilePageProps) {
/>
))}
{topLevelThoughts.length === 0 && (
<Card className="flex items-center justify-center h-48">
<Card
id="profile-card__no-thoughts"
className="flex items-center justify-center h-48"
>
<p className="text-center text-muted-foreground">
This user hasn&apos;t posted any public thoughts yet.
</p>