feat: apply new branding logo across app and pwa assets

This commit is contained in:
2026-03-04 08:21:25 +01:00
parent 4e8c8ebac0
commit 06848d2c3a
37 changed files with 161 additions and 101 deletions

BIN
public/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 889 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 895 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 3.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 3.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 4.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 3.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 4.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 5.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 2.2 MiB

View File

@@ -23,7 +23,12 @@
body {
margin: 0;
padding: 0;
font-family: "Segoe UI", "SF Pro Text", -apple-system, BlinkMacSystemFont, sans-serif;
font-family:
"Segoe UI",
"SF Pro Text",
-apple-system,
BlinkMacSystemFont,
sans-serif;
background: var(--bg);
color: var(--text);
min-height: 100vh;
@@ -60,7 +65,12 @@
inset: 0;
pointer-events: none;
background:
linear-gradient(112deg, rgba(79, 70, 229, 0.24) 0%, rgba(6, 182, 212, 0.2) 30%, transparent 56%),
linear-gradient(
112deg,
rgba(79, 70, 229, 0.24) 0%,
rgba(6, 182, 212, 0.2) 30%,
transparent 56%
),
linear-gradient(248deg, rgba(244, 114, 182, 0.16) 0%, transparent 46%),
repeating-linear-gradient(135deg, rgba(226, 232, 240, 0.03) 0 1px, transparent 1px 11px);
}
@@ -82,6 +92,15 @@
gap: 0.75rem;
}
.brand-logo {
width: 2.4rem;
height: 2.4rem;
border-radius: 0.6rem;
object-fit: cover;
box-shadow: 0 0 20px rgba(34, 211, 238, 0.35);
border: 1px solid rgba(148, 163, 184, 0.35);
}
.menu-btn {
width: 2.25rem;
height: 2.25rem;
@@ -170,7 +189,12 @@
inset: 0;
pointer-events: none;
background:
linear-gradient(160deg, rgba(79, 70, 229, 0.12) 0%, rgba(6, 182, 212, 0.08) 32%, transparent 58%),
linear-gradient(
160deg,
rgba(79, 70, 229, 0.12) 0%,
rgba(6, 182, 212, 0.08) 32%,
transparent 58%
),
linear-gradient(332deg, rgba(244, 114, 182, 0.06) 0%, transparent 42%),
repeating-linear-gradient(135deg, rgba(226, 232, 240, 0.02) 0 1px, transparent 1px 11px);
}
@@ -327,8 +351,9 @@
<div class="header-inner">
<div class="brand">
<button class="menu-btn" id="sidebar-toggle" type="button" aria-label="Menu"></button>
<img class="brand-logo" src="/images/logostripstream.png" alt="StripStream logo" />
<div>
<div class="brand-title">STRIPSTREAM</div>
<div class="brand-title">StripStream</div>
<div class="brand-subtitle">comic reader</div>
</div>
</div>
@@ -358,16 +383,16 @@
<div class="status">● Hors ligne</div>
<h1>Cette page n'est pas encore disponible hors ligne.</h1>
<p>
Tu peux continuer a naviguer sur les pages deja consultees. Cette route sera
disponible hors ligne apres une visite en ligne.
Tu peux continuer a naviguer sur les pages deja consultees. Cette route sera disponible
hors ligne apres une visite en ligne.
</p>
<div class="actions">
<button class="btn btn-secondary" onclick="window.history.back()">Retour</button>
<button class="btn btn-primary" onclick="window.location.reload()">Reessayer</button>
</div>
<div class="hint">
Astuce: visite d'abord Accueil, Bibliotheques, Series et pages de lecture quand tu es
en ligne.
Astuce: visite d'abord Accueil, Bibliotheques, Series et pages de lecture quand tu es en
ligne.
</div>
<div class="footer">StripStream - interface hors ligne</div>
</div>

File diff suppressed because one or more lines are too long

View File

@@ -3,11 +3,11 @@ const fs = require("fs").promises;
const path = require("path");
const sizes = [72, 96, 128, 144, 152, 192, 384, 512];
const inputSvg = path.join(__dirname, "../public/favicon.svg");
const inputAppleSvg = path.join(__dirname, "../public/apple-icon.svg");
const sourceLogo = path.join(__dirname, "../public/images/logostripstream.png");
const outputDir = path.join(__dirname, "../public/images/icons");
const screenshotsDir = path.join(__dirname, "../public/images/screenshots");
const splashDir = path.join(__dirname, "../public/images/splash");
const faviconPath = path.join(__dirname, "../public/favicon.png");
// Configuration des splashscreens pour différents appareils
const splashScreens = [
@@ -24,35 +24,23 @@ const splashScreens = [
async function generateSplashScreens() {
await fs.mkdir(splashDir, { recursive: true });
// Créer le SVG de base pour la splashscreen avec le même style que le favicon
const splashSvg = `
<svg width="100%" height="100%" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect width="100" height="100" fill="#4F46E5"/>
<g transform="translate(25, 20) scale(1.5)">
<!-- Lettre S stylisée -->
<path
d="M21 12C21 10.3431 19.6569 9 18 9H14C12.3431 9 11 10.3431 11 12V12.5C11 14.1569 12.3431 15.5 14 15.5H18C19.6569 15.5 21 16.8431 21 18.5V19C21 20.6569 19.6569 22 18 22H14C12.3431 22 11 20.6569 11 19"
stroke="white"
stroke-width="3"
stroke-linecap="round"
stroke-linejoin="round"
/>
<!-- Points décoratifs -->
<circle cx="11" cy="24" r="1.5" fill="white"/>
<circle cx="21" cy="8" r="1.5" fill="white"/>
</g>
</svg>
`;
for (const screen of splashScreens) {
const outputPath = path.join(splashDir, `splash-${screen.width}x${screen.height}.png`);
const darkOverlay = Buffer.from(
`<svg width="${screen.width}" height="${screen.height}" xmlns="http://www.w3.org/2000/svg">
<rect width="100%" height="100%" fill="rgba(4, 8, 20, 0.22)" />
</svg>`
);
await sharp(Buffer.from(splashSvg))
await sharp(sourceLogo)
.resize(screen.width, screen.height, {
fit: "contain",
background: "#4F46E5",
fit: "cover",
position: "center",
})
.composite([{ input: darkOverlay, blend: "over" }])
.png({
compressionLevel: 9,
})
.png()
.toFile(outputPath);
console.log(`✓ Splashscreen ${screen.name} (${screen.width}x${screen.height}) générée`);
@@ -61,17 +49,19 @@ async function generateSplashScreens() {
async function generateIcons() {
try {
await fs.access(sourceLogo);
// Créer les dossiers de sortie s'ils n'existent pas
await fs.mkdir(outputDir, { recursive: true });
await fs.mkdir(screenshotsDir, { recursive: true });
// Générer les icônes Android (avec bords arrondis)
// Générer les icônes Android
for (const size of sizes) {
const outputPath = path.join(outputDir, `icon-${size}x${size}.png`);
await sharp(inputSvg)
await sharp(sourceLogo)
.resize(size, size, {
fit: "contain",
fit: "cover",
background: { r: 0, g: 0, b: 0, alpha: 0 }, // Fond transparent
})
.png({
@@ -88,9 +78,9 @@ async function generateIcons() {
for (const size of appleSizes) {
const outputPath = path.join(outputDir, `apple-icon-${size}x${size}.png`);
await sharp(inputAppleSvg)
await sharp(sourceLogo)
.resize(size, size, {
fit: "contain",
fit: "cover",
background: { r: 0, g: 0, b: 0, alpha: 0 }, // Fond transparent
})
.png({
@@ -102,26 +92,25 @@ async function generateIcons() {
console.log(`✓ Icône Apple ${size}x${size} générée`);
}
// Générer le favicon principal utilisé par Next metadata
await sharp(sourceLogo)
.resize(64, 64, {
fit: "cover",
})
.png({
compressionLevel: 9,
palette: true,
})
.toFile(faviconPath);
console.log("✓ Favicon principal généré");
// Générer les icônes de raccourcis
const shortcutIcons = [
{ name: "home", icon: "Home" },
{ name: "library", icon: "Library" },
];
const shortcutIcons = ["home", "library"];
for (const shortcut of shortcutIcons) {
const outputPath = path.join(outputDir, `${shortcut.name}.png`);
// Créer une image carrée avec fond indigo et icône blanche
const svg = `
<svg width="96" height="96" viewBox="0 0 96 96" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="96" height="96" rx="20" fill="#4F46E5"/>
<path d="${getIconPath(
shortcut.icon
)}" stroke="white" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
`;
await sharp(Buffer.from(svg))
const outputPath = path.join(outputDir, `${shortcut}.png`);
await sharp(sourceLogo)
.resize(96, 96)
.png({
compressionLevel: 9,
@@ -129,7 +118,7 @@ async function generateIcons() {
})
.toFile(outputPath);
console.log(`✓ Icône de raccourci ${shortcut.name} générée`);
console.log(`✓ Icône de raccourci ${shortcut} générée`);
}
// Générer les screenshots de démonstration
@@ -166,14 +155,4 @@ async function generateIcons() {
}
}
// Fonction helper pour obtenir les chemins SVG des icônes
function getIconPath(iconName) {
const paths = {
Home: "M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6",
Library:
"M4 19.5A2.5 2.5 0 0 1 6.5 17H20M4 19.5A2.5 2.5 0 0 0 6.5 22H20M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H20",
};
return paths[iconName] || "";
}
generateIcons();

View File

@@ -15,7 +15,7 @@ export default async function AccountPage() {
return (
<div className="container mx-auto px-4 py-8">
<div className="max-w-4xl mx-auto space-y-8">
<div className="mx-auto max-w-4xl space-y-8">
<div>
<h1 className="text-3xl font-bold">Mon compte</h1>
<p className="text-muted-foreground mt-2">

View File

@@ -36,8 +36,8 @@ export const metadata: Metadata = {
icons: {
icon: [
{
url: "/favicon.svg",
type: "image/svg+xml",
url: "/favicon.png",
type: "image/png",
},
{ url: "/images/icons/icon-72x72.png", sizes: "72x72", type: "image/png" },
{ url: "/images/icons/icon-96x96.png", sizes: "96x96", type: "image/png" },
@@ -176,7 +176,11 @@ export default async function RootLayout({ children }: { children: React.ReactNo
<AuthProvider>
<I18nProvider locale={locale}>
<PreferencesProvider initialPreferences={preferences}>
<ClientLayout initialLibraries={libraries} initialFavorites={favorites} userIsAdmin={userIsAdmin}>
<ClientLayout
initialLibraries={libraries}
initialFavorites={favorites}
userIsAdmin={userIsAdmin}
>
{children}
</ClientLayout>
</PreferencesProvider>

View File

@@ -1,6 +1,7 @@
"use client";
import { useEffect, useState } from "react";
import Image from "next/image";
const SHOW_DELAY_MS = 140;
@@ -22,8 +23,22 @@ export default function AppLoading() {
}`}
>
<div className="flex w-full max-w-sm flex-col items-center gap-6 text-center">
<div className="space-y-2">
<p className="bg-gradient-to-r from-primary via-cyan-500 to-fuchsia-500 bg-clip-text text-xl font-bold uppercase tracking-[0.12em] text-transparent">
<div className="space-y-3">
<Image
src="/images/logostripstream.png"
alt="StripStream Logo"
width={80}
height={80}
className="mx-auto hidden h-20 w-20 rounded-xl object-cover dark:block"
/>
<Image
src="/images/logostripstream-white.png"
alt="StripStream Logo"
width={80}
height={80}
className="mx-auto h-20 w-20 rounded-xl object-cover dark:hidden"
/>
<p className="bg-gradient-to-r from-primary via-cyan-500 to-fuchsia-500 bg-clip-text text-xl font-bold tracking-[0.08em] text-transparent">
StripStream
</p>
<p className="text-sm text-muted-foreground">Chargement de votre bibliotheque...</p>

View File

@@ -43,21 +43,21 @@ export function LoginContent({ searchParams }: LoginContentProps) {
transition={{ duration: 0.6, delay: 0.2 }}
className="relative z-20 flex items-center text-lg font-medium"
>
<motion.svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="mr-2 h-6 w-6"
whileHover={{ rotate: 180 }}
<motion.img
src="/images/logostripstream.png"
alt="StripStream Logo"
className="mr-3 hidden h-9 w-9 rounded-md object-cover dark:block"
whileHover={{ scale: 1.08, rotate: -3 }}
transition={{ duration: 0.3 }}
>
<path d="M15 6v12a3 3 0 1 0 3-3H6a3 3 0 1 0 3 3V6a3 3 0 1 0-3 3h12a3 3 0 1 0-3-3" />
</motion.svg>
<span className="text-2xl font-bold bg-gradient-to-r from-white to-gray-300 bg-clip-text text-transparent">
/>
<motion.img
src="/images/logostripstream-white.png"
alt="StripStream Logo"
className="mr-3 h-9 w-9 rounded-md object-cover dark:hidden"
whileHover={{ scale: 1.08, rotate: -3 }}
transition={{ duration: 0.3 }}
/>
<span className="text-2xl font-bold bg-gradient-to-r from-primary via-cyan-500 to-fuchsia-500 bg-clip-text text-transparent">
StripStream
</span>
</motion.div>
@@ -83,12 +83,20 @@ export function LoginContent({ searchParams }: LoginContentProps) {
<div className="mx-auto flex w-full flex-col justify-center space-y-6 sm:w-[350px]">
<div className="flex flex-col items-center space-y-4 text-center">
<div className="relative">
<div className="absolute -inset-1 bg-gradient-to-r from-[#4F46E5] to-[#6366F1] rounded-full opacity-75 blur-md animate-pulse"></div>
<div className="relative bg-gradient-to-br from-white to-gray-100 dark:from-slate-800 dark:to-slate-900 rounded-full shadow-xl overflow-hidden w-24 h-24 flex items-center justify-center">
<div className="relative bg-gradient-to-br from-white to-gray-100 dark:from-slate-800 dark:to-slate-900 rounded-full shadow-xl overflow-hidden w-32 h-32 flex items-center justify-center">
<motion.img
src="/images/icons/apple-icon-180x180.png"
src="/images/logostripstream.png"
alt="StripStream Logo"
className="w-[100%] h-[100%] object-cover"
className="hidden h-[100%] w-[100%] object-cover dark:block"
initial={{ scale: 1.8, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
transition={{ duration: 0.5 }}
whileHover={{ scale: 1.05 }}
/>
<motion.img
src="/images/logostripstream-white.png"
alt="StripStream Logo"
className="h-[100%] w-[100%] object-cover dark:hidden"
initial={{ scale: 1.8, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
transition={{ duration: 0.5 }}
@@ -99,7 +107,7 @@ export function LoginContent({ searchParams }: LoginContentProps) {
<motion.h1
initial={{ y: -20 }}
animate={{ y: 0 }}
className="text-3xl font-bold tracking-tight bg-gradient-to-r from-slate-900 to-slate-700 dark:from-white dark:to-gray-300 bg-clip-text text-transparent"
className="text-3xl font-bold tracking-tight bg-gradient-to-r from-primary via-cyan-500 to-fuchsia-500 bg-clip-text text-transparent"
>
{t("login.title")}
</motion.h1>

View File

@@ -6,6 +6,7 @@ import { RefreshButton } from "@/components/library/RefreshButton";
import { PullToRefreshIndicator } from "@/components/common/PullToRefreshIndicator";
import { usePullToRefresh } from "@/hooks/usePullToRefresh";
import { revalidateForRefresh } from "@/app/actions/refresh";
import Image from "next/image";
interface HomeClientWrapperProps {
children: ReactNode;
@@ -48,7 +49,25 @@ export function HomeClientWrapper({ children }: HomeClientWrapperProps) {
isHiding={pullToRefresh.isHiding}
/>
<main className="relative isolate overflow-hidden">
<div className="container mx-auto space-y-6 px-4 pb-8 pt-3">
<div className="pointer-events-none absolute inset-0 z-0 flex items-start justify-center pt-10">
<Image
src="/images/logostripstream.png"
alt=""
width={600}
height={600}
aria-hidden
className="hidden h-auto w-[min(78vw,600px)] opacity-[0.08] saturate-125 dark:block"
/>
<Image
src="/images/logostripstream-white.png"
alt=""
width={600}
height={600}
aria-hidden
className="h-auto w-[min(78vw,600px)] opacity-[0.1] saturate-125 dark:hidden"
/>
</div>
<div className="container relative z-10 mx-auto space-y-6 px-4 pb-8 pt-3">
<div className="flex justify-end">
<RefreshButton libraryId="home" refreshLibrary={handleRefresh} />
</div>

View File

@@ -48,14 +48,14 @@ export function Header({
<div className="mr-2 flex items-center md:mr-4">
<a className="mr-2 flex items-center md:mr-6" href="/">
<span className="inline-flex bg-gradient-to-r from-primary via-cyan-500 to-fuchsia-500 bg-clip-text text-sm font-bold uppercase tracking-[0.1em] text-transparent sm:hidden">
StripStream
<span className="inline-flex bg-gradient-to-r from-primary via-cyan-500 to-fuchsia-500 bg-clip-text text-sm font-bold tracking-[0.06em] text-transparent sm:hidden">
Strip
</span>
<span className="hidden sm:inline-flex flex-col leading-none">
<span className="bg-gradient-to-r from-primary via-cyan-500 to-fuchsia-500 bg-clip-text text-lg font-bold tracking-[0.1em] text-transparent">
STRIPSTREAM
<span className="bg-gradient-to-r from-primary via-cyan-500 to-fuchsia-500 bg-clip-text text-lg font-bold tracking-[0.08em] text-transparent">
StripStream
</span>
<span className="mt-1 text-[10px] font-medium uppercase tracking-[0.28em] text-foreground/70">
<span className="mt-1 text-[10px] font-medium uppercase tracking-[0.22em] text-foreground/70">
comic reader
</span>
</span>

View File

@@ -157,8 +157,18 @@ export function Sidebar({
id="sidebar"
>
<div className="pointer-events-none absolute inset-0 bg-[linear-gradient(160deg,hsl(var(--primary)/0.12)_0%,hsl(192_85%_55%/0.08)_32%,transparent_58%),linear-gradient(332deg,hsl(338_82%_62%/0.06)_0%,transparent_42%),repeating-linear-gradient(135deg,hsl(var(--foreground)/0.02)_0_1px,transparent_1px_11px)]" />
<div className="pointer-events-none absolute inset-0 z-0">
<div
className="hidden h-full w-full bg-center bg-no-repeat opacity-[0.1] [background-size:260%] dark:block"
style={{ backgroundImage: "url('/images/logostripstream.png')" }}
/>
<div
className="h-full w-full bg-center bg-no-repeat opacity-[0.12] [background-size:260%] dark:hidden"
style={{ backgroundImage: "url('/images/logostripstream-white.png')" }}
/>
</div>
<div className="relative flex-1 space-y-4 overflow-y-auto px-3 py-4">
<div className="relative z-10 flex-1 space-y-4 overflow-y-auto px-3 py-4">
<div className="rounded-xl border border-border/50 bg-background/30 p-2">
<div className="space-y-1">
<h2 className="mb-2 px-3 text-xs font-semibold uppercase tracking-[0.2em] text-muted-foreground">

View File

@@ -42,7 +42,7 @@ export function ClientSettings({ initialConfig, initialLibraries }: ClientSettin
return (
<div className="container mx-auto px-4 py-8">
<div className="max-w-4xl mx-auto space-y-8">
<div className="mx-auto max-w-4xl space-y-8">
<h1 className="text-3xl font-bold">{t("settings.title")}</h1>
<Tabs value={activeTab} onValueChange={handleTabChange} className="w-full">