feat: add foodsharing manager branding

Add supplied logos, favicons, PWA metadata, branded login and navigation,
and apply the Foodsharing Manager green palette across existing views.
This commit is contained in:
2026-08-09 00:57:03 +02:00
parent fcbf6bb949
commit 9e0f581e47
47 changed files with 253 additions and 34 deletions

View File

@@ -1,3 +1,4 @@
feat: rename login to foodsharing manager
feat: add foodsharing manager branding
Use the application name consistently in the login view and its test.
Add supplied logos, favicons, PWA metadata, branded login and navigation,
and apply the Foodsharing Manager green palette across existing views.

View File

@@ -0,0 +1,41 @@
# Foodsharing Manager Logo-Assets
## Empfohlene Dateien
### Klassische Webseite
- `favicon/favicon.ico`
- `favicon/favicon-32x32.png`
- `mobile/apple-touch-icon-180x180.png`
- `pwa/site.webmanifest`
### PWA / Android
- `pwa/android-chrome-192x192.png`
- `pwa/android-chrome-512x512.png`
- `pwa/maskable-icon-512x512.png`
### Desktop-App
- Windows: `desktop/app-icon.ico`
- macOS: `desktop/app-icon.icns`
- Linux / Electron / Tauri: `desktop/app-icon-512x512.png` oder `app-icon-1024x1024.png`
### Website-Header
- `web/logo-horizontal-original.png`
- alternativ `web/logo-horizontal-512px.png` oder `web/logo-horizontal-1024px.png`
### Social Media / Link-Vorschau
- `social/og-image-1200x630.png`
- kleinere Dateigröße: `social/og-image-1200x630.jpg`
## Beispiel für HTML
```html
<link rel="icon" href="/favicon/favicon.ico" sizes="any">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png">
<link rel="apple-touch-icon" href="/mobile/apple-touch-icon-180x180.png">
<link rel="manifest" href="/pwa/site.webmanifest">
<meta name="theme-color" content="#248A3D">
<meta property="og:image" content="/social/og-image-1200x630.png">
```
Hinweis: Das Ausgangslogo ist ein Rasterbild. Für sehr große Druckprodukte wäre später eine echte SVG-/Vektorversion sinnvoll.

Binary file not shown.

After

Width:  |  Height:  |  Size: 483 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 832 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

View File

@@ -0,0 +1,27 @@
{
"name": "Foodsharing Manager",
"short_name": "FS Manager",
"icons": [
{
"src": "/pwa/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
"src": "/pwa/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
"src": "/pwa/maskable-icon-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
],
"theme_color": "#248A3D",
"background_color": "#F7FBF5",
"display": "standalone"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 423 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 368 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 516 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
public/branding/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

View File

@@ -1,16 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<html lang="de">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<link rel="icon" href="%PUBLIC_URL%/branding/favicon.ico" sizes="any" />
<link rel="icon" type="image/png" sizes="32x32" href="%PUBLIC_URL%/branding/favicon-32x32.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="theme-color" content="#248a3d" />
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<meta name="description" content="Foodsharing Manager für Slot-Buchungen, Betriebsmonitoring und Abhol-Journal." />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/branding/apple-touch-icon.png" />
<meta property="og:title" content="Foodsharing Manager" />
<meta property="og:description" content="Slot-Buchungen, Betriebsmonitoring und Abhol-Journal für Foodsharing." />
<meta property="og:image" content="%PUBLIC_URL%/branding/og-image.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/

View File

@@ -1,25 +1,25 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"short_name": "Foodsharing Manager",
"name": "Foodsharing Manager",
"icons": [
{
"src": "favicon.ico",
"src": "branding/favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"src": "branding/android-chrome-192x192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"src": "branding/android-chrome-512x512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
"theme_color": "#248a3d",
"background_color": "#f4f8f1"
}

View File

@@ -1,12 +1,129 @@
/* App.css - Zusätzliche Stilregeln für die Pickup-Konfigurations-App */
/* App.css - gemeinsame Oberflaechen- und Markenstile */
:root {
--brand-green: #248a3d;
--brand-green-dark: #17652e;
--brand-green-soft: #edf7ed;
--brand-orange: #f6a51b;
--brand-ink: #39424e;
--brand-surface: #f4f8f1;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
background-color: #f5f7fa;
background-color: var(--brand-surface);
margin: 0;
padding: 0;
}
.foodsharing-login {
display: flex;
min-height: 100vh;
align-items: center;
justify-content: center;
padding: 1.5rem;
background: var(--brand-surface);
}
.foodsharing-login__card {
display: grid;
gap: 1.5rem;
width: 100%;
max-width: 28rem;
padding: 2.25rem;
border: 1px solid #d8e7d6;
border-top: 4px solid var(--brand-green);
border-radius: 10px;
background: #fff;
box-shadow: 0 18px 42px rgba(36, 80, 39, 0.12);
}
.foodsharing-login__logo {
display: block;
width: min(100%, 21rem);
height: auto;
margin: 0 auto 1.5rem;
}
.foodsharing-login__title {
margin: 0 0 0.45rem;
color: var(--brand-ink);
font-size: 1.35rem;
font-weight: 700;
text-align: center;
}
.foodsharing-login__intro {
margin: 0 0 1.5rem;
color: #64706a;
font-size: 0.875rem;
line-height: 1.5;
text-align: center;
}
.foodsharing-login__label {
display: block;
margin-bottom: 0.35rem;
color: var(--brand-ink);
font-size: 0.875rem;
font-weight: 600;
}
.foodsharing-login__input {
box-sizing: border-box;
width: 100%;
padding: 0.65rem 0.75rem;
border: 1px solid #cddacb;
border-radius: 5px;
color: var(--brand-ink);
background: #fff;
}
.foodsharing-login__input:focus {
border-color: var(--brand-green);
outline: none;
box-shadow: 0 0 0 3px rgba(36, 138, 61, 0.18);
}
.foodsharing-login__submit {
width: 100%;
padding: 0.7rem 1rem;
border: 0;
border-radius: 5px;
color: #fff;
background: var(--brand-green);
font-weight: 700;
}
.foodsharing-login__submit:hover {
background: var(--brand-green-dark);
}
.foodsharing-login__error {
margin-bottom: 1.25rem;
padding: 0.7rem 0.85rem;
border: 1px solid #edb4a8;
border-radius: 5px;
color: #9b2c1f;
background: #fff1ed;
font-size: 0.875rem;
}
/* Keep the existing Tailwind-based views on the same brand palette. */
*[class~="bg-blue-50"] { background-color: var(--brand-green-soft) !important; }
*[class~="bg-gray-100"] { background-color: var(--brand-surface) !important; }
*[class~="bg-blue-100"] { background-color: #dcefdc !important; }
*[class~="bg-blue-500"], *[class~="bg-blue-600"] { background-color: var(--brand-green) !important; }
*[class~="text-blue-600"], *[class~="text-blue-700"], *[class~="text-blue-800"], *[class~="text-blue-900"] { color: var(--brand-green-dark) !important; }
*[class~="border-blue-100"], *[class~="border-blue-200"] { border-color: #cce2ca !important; }
*[class~="border-blue-500"], *[class~="border-blue-600"] { border-color: var(--brand-green) !important; }
*[class~="hover:bg-blue-600"]:hover, *[class~="hover:bg-blue-700"]:hover { background-color: var(--brand-green-dark) !important; }
*[class~="hover:bg-blue-50"]:hover, *[class~="hover:bg-blue-100"]:hover { background-color: var(--brand-green-soft) !important; }
*[class~="hover:text-blue-600"]:hover, *[class~="hover:text-blue-700"]:hover, *[class~="hover:text-blue-800"]:hover { color: var(--brand-green-dark) !important; }
*[class~="hover:border-blue-400"]:hover, *[class~="hover:border-blue-500"]:hover { border-color: var(--brand-green) !important; }
*[class~="focus:border-blue-500"]:focus { border-color: var(--brand-green) !important; }
*[class~="focus:ring-blue-500"]:focus, *[class~="focus:ring-blue-400"]:focus, *[class~="focus:ring-blue-300"]:focus, *[class~="focus:ring-blue-200"]:focus { --tw-ring-color: rgba(36, 138, 61, 0.35) !important; }
.App {
min-height: 100vh;
padding: 1rem;

View File

@@ -3,6 +3,6 @@ import App from './App';
test('zeigt das Login-Formular an, wenn keine Session aktiv ist', () => {
render(<App />);
expect(screen.getByText(/Foodsharing Manager Login/i)).toBeInTheDocument();
expect(screen.getByRole('heading', { name: 'Anmelden' })).toBeInTheDocument();
expect(screen.getByLabelText(/E-Mail/i)).toBeInTheDocument();
});

View File

@@ -4,9 +4,9 @@ const LoginView = ({ credentials, onCredentialsChange, error, loading, initializ
};
return (
<div className="min-h-screen flex items-center justify-center bg-gray-100">
<div className="foodsharing-login">
<form
className="bg-white shadow-lg rounded-lg p-8 w-full max-w-md space-y-6"
className="foodsharing-login__card"
onSubmit={(event) => {
if (initializing) {
event.preventDefault();
@@ -16,20 +16,21 @@ const LoginView = ({ credentials, onCredentialsChange, error, loading, initializ
}}
>
<div>
<h1 className="text-2xl font-bold mb-2 text-center text-gray-800">Foodsharing Manager Login</h1>
<p className="text-sm text-gray-500 text-center">
<img className="foodsharing-login__logo" src="/branding/logo-horizontal.png" alt="Foodsharing Manager" />
<h1 className="foodsharing-login__title">Anmelden</h1>
<p className="foodsharing-login__intro">
Die Anwendung authentifiziert sich direkt bei foodsharing.de. Bitte verwende deine persönlichen Login-Daten.
</p>
</div>
{error && (
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative">
<span className="block sm:inline">{error}</span>
<div className="foodsharing-login__error">
<span>{error}</span>
</div>
)}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1" htmlFor="email">
<label className="foodsharing-login__label" htmlFor="email">
E-Mail
</label>
<input
@@ -38,13 +39,13 @@ const LoginView = ({ credentials, onCredentialsChange, error, loading, initializ
name="email"
value={credentials.email}
onChange={handleChange('email')}
className="border rounded p-2 w-full focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
className="foodsharing-login__input"
required
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1" htmlFor="password">
<label className="foodsharing-login__label" htmlFor="password">
Passwort
</label>
<input
@@ -53,7 +54,7 @@ const LoginView = ({ credentials, onCredentialsChange, error, loading, initializ
name="password"
value={credentials.password}
onChange={handleChange('password')}
className="border rounded p-2 w-full focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
className="foodsharing-login__input"
required
/>
</div>
@@ -61,7 +62,7 @@ const LoginView = ({ credentials, onCredentialsChange, error, loading, initializ
<button
type="submit"
disabled={loading || initializing}
className="w-full bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:opacity-60"
className="foodsharing-login__submit"
>
{loading || initializing ? 'Anmeldung läuft...' : 'Anmelden'}
</button>

View File

@@ -21,6 +21,26 @@
background: #fff;
}
.app-navigation__brand {
display: inline-flex;
align-items: center;
gap: 0.45rem;
margin-right: 0.5rem;
padding-right: 1rem;
border-right: 1px solid #d8e7d6;
color: #17652e;
font-size: 0.875rem;
font-weight: 700;
text-decoration: none;
white-space: nowrap;
}
.app-navigation__brand-mark {
width: 1.9rem;
height: 1.9rem;
object-fit: contain;
}
.app-navigation__desktop-link {
border-radius: 4px;
padding: 0.45rem 0.65rem;
@@ -67,6 +87,12 @@
white-space: nowrap;
}
.app-navigation__mobile-header .app-navigation__brand-mark {
width: 1.75rem;
height: 1.75rem;
margin-right: -0.25rem;
}
.app-navigation__menu-toggle {
display: grid;
flex: 0 0 auto;

View File

@@ -69,6 +69,7 @@ const NavigationTabs = ({
return (
<nav className={`app-navigation ${hasNavigation ? '' : 'app-navigation--compact'}`} aria-label="Navigation">
<div className="app-navigation__mobile-header">
<img className="app-navigation__brand-mark" src="/branding/logo-mark.png" alt="" aria-hidden="true" />
<span className="app-navigation__product-name">Foodsharing Manager</span>
<button
type="button"
@@ -142,6 +143,10 @@ const NavigationTabs = ({
{hasNavigation && (
<div className="app-navigation__desktop">
<Link to="/" className="app-navigation__brand" aria-label="Foodsharing Manager Startseite">
<img className="app-navigation__brand-mark" src="/branding/logo-mark.png" alt="" aria-hidden="true" />
<span>Foodsharing Manager</span>
</Link>
{tabs.map((tab) => renderLink(tab, 'app-navigation__desktop-link'))}
</div>
)}

View File

@@ -112,7 +112,7 @@ const RangePickerModal = ({
<span>{date.getDate()}</span>
{(hasFoodsharingPickup || hasManagerPlan) && (
<span className="absolute bottom-0 flex gap-0.5">
{hasFoodsharingPickup && <span className="h-1.5 w-1.5 rounded-full bg-blue-600" />}
{hasFoodsharingPickup && <span className="h-1.5 w-1.5 rounded-full bg-green-600" />}
{hasManagerPlan && <span className="h-1.5 w-1.5 rounded-full bg-emerald-600" />}
</span>
)}
@@ -154,7 +154,7 @@ const RangePickerModal = ({
/>
<div className="mt-3 flex flex-wrap items-center gap-3 px-3 text-xs text-gray-600">
<span className="inline-flex items-center gap-1">
<span className="h-2 w-2 rounded-full bg-blue-600" />
<span className="h-2 w-2 rounded-full bg-green-600" />
Foodsharing eingetragen
</span>
<span className="inline-flex items-center gap-1">
@@ -173,7 +173,7 @@ const RangePickerModal = ({
</button>
<button
type="button"
className="text-sm font-semibold text-white bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded-md"
className="text-sm font-semibold text-white bg-green-600 hover:bg-green-700 px-4 py-2 rounded-md"
onClick={onClose}
>
Fertig