feat: Implement TaskFlow logos and icons throughout the application
continuous-integration/drone/push Build is passing

- Add favicon files (16x16 to 256x256, .ico)
- Add PWA icons (128, 256, 512, 1024)
- Add apple-touch-icon for iOS
- Add transparent icons for loading screens
- Add horizontal logo with tagline for auth page
- Update HTML head with proper favicon links
- Update PWA manifest with correct icon references
- Update sidebar header with branded icon
- Update mobile header with branded icon
- Update auth/login pages with logo display
- Update loading state with branded splash screen
- Update 404 page with logo header
- Update email template to use proper logo URL
- Update service worker and push notifications to use correct icons
This commit is contained in:
Paul Nothaft
2026-01-19 21:40:09 +01:00
parent 4b51e9f05e
commit 88cd2a5c88
68 changed files with 72 additions and 32 deletions
+9 -3
View File
@@ -15,9 +15,15 @@
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="apple-mobile-web-app-title" content="TaskFlow" />
<!-- Icons -->
<link rel="icon" type="image/png" href="/favicon.png?v=4" />
<link rel="apple-touch-icon" href="/favicon.png?v=4" />
<!-- Favicons -->
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="48x48" href="/favicon-48x48.png" />
<link rel="icon" type="image/png" sizes="64x64" href="/favicon-64x64.png" />
<link rel="icon" type="image/png" sizes="128x128" href="/favicon-128x128.png" />
<link rel="icon" type="image/png" sizes="256x256" href="/favicon-256x256.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 458 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 480 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 419 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 773 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 MiB

+22 -7
View File
@@ -1,7 +1,7 @@
{
"name": "TaskFlow",
"short_name": "TaskFlow",
"description": "Gamified task management with AI assistance",
"description": "Gamified task management with AI assistance. Focus. Flow. AI.",
"start_url": "/",
"display": "standalone",
"background_color": "#0f0f23",
@@ -9,16 +9,31 @@
"orientation": "portrait-primary",
"icons": [
{
"src": "/favicon.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
"src": "/icon-128.png",
"sizes": "128x128",
"type": "image/png"
},
{
"src": "/favicon.png",
"src": "/icon-256.png",
"sizes": "256x256",
"type": "image/png"
},
{
"src": "/icon-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
"purpose": "any"
},
{
"src": "/icon-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/icon-1024.png",
"sizes": "1024x1024",
"type": "image/png"
}
],
"categories": ["productivity", "utilities"],
+2 -2
View File
@@ -29,8 +29,8 @@ self.addEventListener('push', (event) => {
let data = {
title: 'TaskFlow',
body: 'You have a new notification',
icon: '/favicon.png',
badge: '/favicon.png',
icon: '/icon-256.png',
badge: '/icon-128.png',
tag: 'taskflow-notification',
data: {}
};
+11 -3
View File
@@ -285,8 +285,14 @@ function AppContent() {
if (isLoadingUser) {
return (
<div className="flex items-center justify-center min-h-screen bg-background">
<Loader2 className="h-8 w-8 animate-spin text-primary" />
<div className="flex flex-col items-center justify-center min-h-screen bg-background gap-6">
<div className="w-20 h-20 rounded-2xl bg-gradient-to-br from-violet-600 to-indigo-600 flex items-center justify-center shadow-2xl animate-pulse">
<img src="/icon-transparent-256.png" alt="TaskFlow" className="w-14 h-14 object-contain" />
</div>
<div className="flex items-center gap-2">
<Loader2 className="h-5 w-5 animate-spin text-primary" />
<span className="text-muted-foreground text-sm">Loading TaskFlow...</span>
</div>
</div>
);
}
@@ -341,7 +347,9 @@ function AppContent() {
<header className="sticky top-0 z-50 flex items-center gap-3 px-4 py-3 bg-background/80 backdrop-blur-xl border-b border-border/50 md:hidden">
<SidebarTrigger className="h-9 w-9" />
<div className="flex items-center gap-2">
<img src="/favicon.png" alt="Logo" className="w-7 h-7 rounded-lg" />
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-violet-600 to-indigo-600 flex items-center justify-center shadow-md">
<img src="/icon-transparent-128.png" alt="TaskFlow" className="w-6 h-6 object-contain" />
</div>
<span className="font-bold text-lg">{t('app.title')}</span>
</div>
</header>
+3 -3
View File
@@ -72,13 +72,13 @@ export function AppSidebar({ user, ...props }: AppSidebarProps) {
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton size="lg" className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground h-14">
<div className="flex aspect-square size-9 items-center justify-center rounded-xl overflow-hidden shadow-lg bg-transparent">
<img src="/favicon.png" alt="Logo" className="w-full h-full object-cover" />
<div className="flex aspect-square size-9 items-center justify-center rounded-xl overflow-hidden shadow-lg bg-gradient-to-br from-violet-600 to-indigo-600">
<img src="/icon-transparent-128.png" alt="TaskFlow" className="w-7 h-7 object-contain" />
</div>
{state !== 'collapsed' && (
<div className="grid flex-1 text-left text-sm leading-tight ml-2">
<span className="truncate font-bold text-base">{t('app.title')}</span>
<span className="truncate text-xs opacity-70">Personal</span>
<span className="truncate text-xs opacity-70">Focus. Flow. AI.</span>
</div>
)}
</SidebarMenuButton>
+13 -6
View File
@@ -145,10 +145,13 @@ export default function AuthPage() {
return (
<div className="min-h-screen grid lg:grid-cols-2 dark">
<div className="hidden lg:flex flex-col justify-center items-center bg-zinc-900 p-12 text-white">
<div className="max-w-md space-y-4 text-center">
<div className="mb-8">
<img src="/favicon.png" alt="Logo" className="w-24 h-24" />
<div className="hidden lg:flex flex-col justify-center items-center bg-gradient-to-br from-zinc-900 via-zinc-900 to-violet-950 p-12 text-white">
<div className="max-w-md space-y-6 text-center">
<div className="mb-8 flex flex-col items-center">
<div className="w-28 h-28 rounded-2xl bg-gradient-to-br from-violet-600 to-indigo-600 flex items-center justify-center shadow-2xl mb-6">
<img src="/icon-transparent-256.png" alt="TaskFlow" className="w-20 h-20 object-contain" />
</div>
<img src="/logo-horizontal-tagline-transparent.png" alt="TaskFlow - Focus. Flow. AI." className="h-12 object-contain" />
</div>
<h1 className="text-4xl font-bold tracking-tight">{t('auth.heroTitle')}</h1>
<p className="text-lg text-zinc-400">
@@ -162,7 +165,9 @@ export default function AuthPage() {
<Card className="w-full max-w-md shadow-xl border-border/50">
<CardHeader className="text-center space-y-2">
<div className="lg:hidden mx-auto mb-4">
<img src="/favicon.png" alt="Logo" className="w-12 h-12 mx-auto" />
<div className="w-14 h-14 rounded-xl bg-gradient-to-br from-violet-600 to-indigo-600 flex items-center justify-center shadow-lg mx-auto">
<img src="/icon-transparent-128.png" alt="TaskFlow" className="w-10 h-10 object-contain" />
</div>
</div>
<CardTitle className="text-2xl font-bold">{t('auth.2faVerification')}</CardTitle>
<CardDescription>
@@ -194,7 +199,9 @@ export default function AuthPage() {
<Card className="w-full max-w-md shadow-xl border-border/50">
<CardHeader className="text-center space-y-2">
<div className="lg:hidden mx-auto mb-4">
<img src="/favicon.png" alt="Logo" className="w-12 h-12 mx-auto" />
<div className="w-14 h-14 rounded-xl bg-gradient-to-br from-violet-600 to-indigo-600 flex items-center justify-center shadow-lg mx-auto">
<img src="/icon-transparent-128.png" alt="TaskFlow" className="w-10 h-10 object-contain" />
</div>
</div>
<CardTitle className="text-2xl font-bold">{t('auth.welcomeBack')}</CardTitle>
<CardDescription>
+8 -1
View File
@@ -14,7 +14,14 @@ import { Link } from "wouter";
export default function NotFound() {
return (
<div className="min-h-screen w-full flex items-center justify-center bg-background p-4">
<div className="min-h-screen w-full flex flex-col items-center justify-center bg-background p-4">
{/* Logo */}
<div className="mb-8 flex items-center gap-3">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-violet-600 to-indigo-600 flex items-center justify-center shadow-lg">
<img src="/icon-transparent-128.png" alt="TaskFlow" className="w-7 h-7 object-contain" />
</div>
<span className="text-xl font-bold">TaskFlow</span>
</div>
<Card className="w-full max-w-2xl mx-auto shadow-xl border-dashed border-2 overflow-hidden bg-card text-card-foreground">
<div className="md:flex">
<div className="md:w-1/2 bg-muted/30 flex items-center justify-center p-8">
Binary file not shown.

After

Width:  |  Height:  |  Size: 773 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 566 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 925 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 458 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 480 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 MiB

+2 -5
View File
@@ -12,7 +12,7 @@ export function generateEmailHtml(language: string, content: {
: "This email was sent automatically. Please do not reply.";
const siteUrl = process.env.VITE_PUBLIC_APP_URL || "http://localhost:5001";
const logoUrl = `${siteUrl}/favicon.png`;
const logoUrl = `${siteUrl}/icon-256.png`;
return `
<!DOCTYPE html>
@@ -103,10 +103,7 @@ export function generateEmailHtml(language: string, content: {
<body>
<div class="container">
<div class="logo">
<!-- Trying to link to external URL for logo if accessible, otherwise alt text plays role -->
<img src="https://raw.githubusercontent.com/shadcn-ui/ui/main/apps/www/public/favicon.ico" alt="TaskFlow" style="border-radius: 8px;" width="48" height="48">
<!-- Ideally we host the logo. For localhost, external clients won't see localhost images. I'll use a placeholder or assume the user will configure a real URL in prod.
For now, I'll use a generic pleasing icon or text if image breaks, but let's try to pass the favicon. -->
<img src="${logoUrl}" alt="TaskFlow" style="border-radius: 12px;" width="56" height="56">
</div>
<div class="card">
<h1>${content.title}</h1>
+2 -2
View File
@@ -207,8 +207,8 @@ export async function sendPushNotification(
const notificationPayload = JSON.stringify({
title: payload.title,
body: payload.body,
icon: payload.icon || '/favicon.png',
badge: payload.badge || '/favicon.png',
icon: payload.icon || '/icon-256.png',
badge: payload.badge || '/icon-128.png',
tag: payload.tag || `taskflow-${Date.now()}`,
data: payload.data || {}
});