BloomFeed Patch v0.23

This commit is contained in:
Esteban
2026-07-04 23:29:59 +02:00
parent 487fdb9f75
commit eaebadcfa6
8 changed files with 344 additions and 49 deletions
+4
View File
@@ -34,6 +34,10 @@ return [
'screenshot_reader' => 'Integrierter Reader', 'screenshot_reader' => 'Integrierter Reader',
'screenshot_catalog' => 'Feed-Katalog mit einem Klick', 'screenshot_catalog' => 'Feed-Katalog mit einem Klick',
'screenshot_notifiers' => 'Discord- und Webhook-Benachrichtigungen', 'screenshot_notifiers' => 'Discord- und Webhook-Benachrichtigungen',
'badge_self_hosted' => 'Selbst gehostet',
'badge_open_source' => 'Open Source',
'badge_mit_license' => 'MIT-Lizenz',
'badge_four_languages' => '4 Sprachen',
// Authentifizierung // Authentifizierung
'login_title' => 'Anmelden', 'login_title' => 'Anmelden',
+4
View File
@@ -34,6 +34,10 @@ return [
'screenshot_reader' => 'Built-in reader', 'screenshot_reader' => 'Built-in reader',
'screenshot_catalog' => 'One-click feed catalog', 'screenshot_catalog' => 'One-click feed catalog',
'screenshot_notifiers' => 'Discord & webhook notifiers', 'screenshot_notifiers' => 'Discord & webhook notifiers',
'badge_self_hosted' => 'Self-hosted',
'badge_open_source' => 'Open-source',
'badge_mit_license' => 'MIT license',
'badge_four_languages' => '4 languages',
// Authentication // Authentication
'login_title' => 'Log in', 'login_title' => 'Log in',
+4
View File
@@ -34,6 +34,10 @@ return [
'screenshot_reader' => 'Lector integrado', 'screenshot_reader' => 'Lector integrado',
'screenshot_catalog' => 'Catálogo de fuentes en un clic', 'screenshot_catalog' => 'Catálogo de fuentes en un clic',
'screenshot_notifiers' => 'Notificadores de Discord y webhook', 'screenshot_notifiers' => 'Notificadores de Discord y webhook',
'badge_self_hosted' => 'Autoalojado',
'badge_open_source' => 'Código abierto',
'badge_mit_license' => 'Licencia MIT',
'badge_four_languages' => '4 idiomas',
// Autenticación // Autenticación
'login_title' => 'Iniciar sesión', 'login_title' => 'Iniciar sesión',
+4
View File
@@ -34,6 +34,10 @@ return [
'screenshot_reader' => 'Lecteur intégré', 'screenshot_reader' => 'Lecteur intégré',
'screenshot_catalog' => 'Catalogue de flux en un clic', 'screenshot_catalog' => 'Catalogue de flux en un clic',
'screenshot_notifiers' => 'Notifieurs Discord et webhook', 'screenshot_notifiers' => 'Notifieurs Discord et webhook',
'badge_self_hosted' => 'Auto-hébergé',
'badge_open_source' => 'Open-source',
'badge_mit_license' => 'Licence MIT',
'badge_four_languages' => '4 langues',
// Authentification // Authentification
'login_title' => 'Connexion', 'login_title' => 'Connexion',
+233 -26
View File
@@ -102,6 +102,17 @@ h1, h2, h3 {
50% { transform: translateY(-8px); } 50% { transform: translateY(-8px); }
} }
@keyframes icon-float {
0%, 100% { transform: translateY(0) rotate(0deg); }
50% { transform: translateY(-14px) rotate(6deg); }
}
@keyframes orb-drift {
0%, 100% { transform: translate(0, 0) scale(1); }
33% { transform: translate(30px, -20px) scale(1.08); }
66% { transform: translate(-20px, 18px) scale(0.95); }
}
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
*, *,
*::before, *::before,
@@ -259,18 +270,110 @@ h1, h2, h3 {
animation: fade-up var(--t-slow) both; animation: fade-up var(--t-slow) both;
} }
/* ---------- Scroll-reveal utility ---------- */
.reveal {
opacity: 0;
transform: translateY(28px);
transition: opacity 700ms var(--ease-out), transform 700ms var(--ease-out);
transition-delay: var(--reveal-delay, 0ms);
}
.reveal.is-visible {
opacity: 1;
transform: translateY(0);
}
/* ---------- Hero / marketing ---------- */ /* ---------- Hero / marketing ---------- */
.hero { .hero {
position: relative;
background: var(--gradient-hero); background: var(--gradient-hero);
padding: var(--space-8) var(--space-5) var(--space-7); padding: var(--space-8) var(--space-5) var(--space-7);
overflow: hidden; overflow: hidden;
} }
.hero-inner { .hero-orbs {
max-width: 780px; position: absolute;
inset: 0;
z-index: 0;
pointer-events: none;
}
.orb {
position: absolute;
border-radius: 50%;
filter: blur(40px);
opacity: 0.55;
background: var(--gradient-accent);
animation: orb-drift 16s ease-in-out infinite;
}
.orb-1 {
width: 340px;
height: 340px;
top: -120px;
left: -80px;
animation-duration: 18s;
}
.orb-2 {
width: 260px;
height: 260px;
bottom: -100px;
right: 6%;
animation-duration: 22s;
animation-delay: -6s;
background: linear-gradient(135deg, var(--green) 0%, var(--sun-bright) 100%);
opacity: 0.28;
}
.orb-3 {
width: 180px;
height: 180px;
top: 20%;
right: 22%;
animation-duration: 14s;
animation-delay: -3s;
opacity: 0.3;
}
.hero-float-icon {
position: absolute;
z-index: 1;
display: flex;
align-items: center;
justify-content: center;
width: 56px;
height: 56px;
border-radius: var(--radius-lg);
background: var(--surface);
border: 1px solid var(--border);
box-shadow: var(--shadow-md);
color: var(--accent);
font-size: 1.4rem;
animation: icon-float 6s ease-in-out infinite;
pointer-events: none;
}
.hero-float-icon-1 { top: 12%; left: 6%; animation-duration: 7s; }
.hero-float-icon-2 { top: 66%; left: 11%; animation-duration: 8s; animation-delay: -2s; }
.hero-float-icon-3 { top: 18%; right: 8%; animation-duration: 6.5s; animation-delay: -4s; }
.hero-float-icon-4 { top: 70%; right: 14%; animation-duration: 7.5s; animation-delay: -1s; }
.hero-grid {
position: relative;
z-index: 2;
max-width: 1180px;
margin: 0 auto; margin: 0 auto;
text-align: center; display: grid;
grid-template-columns: 1.05fr 0.95fr;
align-items: center;
gap: var(--space-7);
}
.hero-inner {
text-align: left;
animation: fade-up 700ms var(--ease-out) both; animation: fade-up 700ms var(--ease-out) both;
} }
@@ -303,15 +406,96 @@ h1, h2, h3 {
.hero-lede { .hero-lede {
font-size: var(--font-md); font-size: var(--font-md);
color: var(--text-muted); color: var(--text-muted);
max-width: 640px; max-width: 560px;
margin: 0 auto var(--space-6); margin: 0 0 var(--space-6);
} }
.hero-cta { .hero-cta {
display: flex; display: flex;
justify-content: center; justify-content: flex-start;
gap: var(--space-3); gap: var(--space-3);
flex-wrap: wrap; flex-wrap: wrap;
margin-bottom: var(--space-6);
}
.trust-badges {
display: flex;
flex-wrap: wrap;
gap: var(--space-2);
}
.trust-badge {
display: inline-flex;
align-items: center;
gap: 8px;
font-size: var(--font-xs);
font-weight: 700;
color: var(--text-muted);
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-full);
padding: 8px 14px;
}
.trust-badge i {
color: var(--accent);
}
.hero-visual {
position: relative;
animation: fade-up 800ms var(--ease-out) both;
animation-delay: 150ms;
}
.browser-frame {
border-radius: var(--radius-lg);
overflow: hidden;
border: 1px solid var(--border);
box-shadow: var(--shadow-lg);
background: var(--surface);
transition: transform var(--t-base);
}
.hero-visual .browser-frame:hover {
transform: translateY(-6px) rotate(0.3deg);
}
.browser-frame-bar {
display: flex;
align-items: center;
gap: 7px;
padding: 10px 14px;
background: var(--surface-alt);
border-bottom: 1px solid var(--border);
}
.browser-dot {
width: 10px;
height: 10px;
border-radius: 50%;
}
.browser-dot-red { background: #ec6a5e; }
.browser-dot-yellow { background: #f4bf4f; }
.browser-dot-green { background: #61c454; }
.browser-frame-img,
.browser-frame-img-light,
.browser-frame-img-dark {
width: 100%;
display: block;
}
.browser-frame-img-dark {
display: none;
}
[data-theme="dark"] .browser-frame-img-light {
display: none;
}
[data-theme="dark"] .browser-frame-img-dark {
display: block;
} }
.feature-grid { .feature-grid {
@@ -331,14 +515,9 @@ h1, h2, h3 {
padding: var(--space-6); padding: var(--space-6);
position: relative; position: relative;
overflow: hidden; overflow: hidden;
animation: fade-up 600ms var(--ease-out) both;
transition: transform var(--t-base), box-shadow var(--t-base), border-color var(--t-base); transition: transform var(--t-base), box-shadow var(--t-base), border-color var(--t-base);
} }
.feature-card:nth-child(2) { animation-delay: 120ms; }
.feature-card:nth-child(3) { animation-delay: 240ms; }
.feature-card:nth-child(4) { animation-delay: 360ms; }
.feature-card::before { .feature-card::before {
content: ""; content: "";
position: absolute; position: absolute;
@@ -351,7 +530,7 @@ h1, h2, h3 {
} }
.feature-card:hover { .feature-card:hover {
transform: translateY(-6px); transform: translateY(-8px);
box-shadow: var(--shadow-md); box-shadow: var(--shadow-md);
border-color: var(--accent-soft-strong); border-color: var(--accent-soft-strong);
} }
@@ -361,14 +540,26 @@ h1, h2, h3 {
} }
.feature-icon { .feature-icon {
font-size: 2rem; display: flex;
margin-bottom: var(--space-3); align-items: center;
display: inline-block; justify-content: center;
width: 76px;
height: 76px;
border-radius: var(--radius);
font-size: 2.1rem;
margin-bottom: var(--space-4);
color: var(--accent-contrast);
background: var(--gradient-accent);
box-shadow: var(--shadow-accent);
transition: transform var(--t-base); transition: transform var(--t-base);
} }
.feature-icon-2 { background: linear-gradient(135deg, var(--green) 0%, #7fd19e 100%); }
.feature-icon-3 { background: linear-gradient(135deg, #6ba7e8 0%, #a3c9f7 100%); }
.feature-icon-4 { background: linear-gradient(135deg, #e88a6b 0%, var(--sun-bright) 100%); }
.feature-card:hover .feature-icon { .feature-card:hover .feature-icon {
transform: scale(1.15) rotate(-4deg); transform: scale(1.12) rotate(-4deg);
} }
.feature-card h3 { .feature-card h3 {
@@ -412,24 +603,16 @@ h1, h2, h3 {
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
gap: var(--space-4); gap: var(--space-4);
animation: fade-up 600ms var(--ease-out) both;
} }
.screenshot-main { .screenshot-main {
width: 100%; width: 100%;
aspect-ratio: 16 / 10; aspect-ratio: 16 / 10;
border-radius: var(--radius-lg);
overflow: hidden;
border: 1px solid var(--border);
box-shadow: var(--shadow-lg);
background: var(--surface);
} }
.screenshot-main img { .screenshot-main .browser-frame-img {
width: 100%; height: calc(100% - 33px);
height: 100%;
object-fit: cover; object-fit: cover;
display: block;
} }
.screenshot-thumbs { .screenshot-thumbs {
@@ -475,6 +658,30 @@ h1, h2, h3 {
margin: 0; margin: 0;
} }
@media (max-width: 900px) {
.hero-grid {
grid-template-columns: 1fr;
}
.hero-inner {
text-align: center;
}
.hero-cta,
.trust-badges {
justify-content: center;
}
.hero-lede {
margin-left: auto;
margin-right: auto;
}
.hero-float-icon {
display: none;
}
}
/* ---------- Page docs / self-hosting ---------- */ /* ---------- Page docs / self-hosting ---------- */
.docs-list { .docs-list {
+33
View File
@@ -0,0 +1,33 @@
(function () {
document.addEventListener('DOMContentLoaded', function () {
var targets = document.querySelectorAll('.reveal');
if (!targets.length) {
return;
}
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches || !('IntersectionObserver' in window)) {
targets.forEach(function (el) { el.classList.add('is-visible'); });
return;
}
var observer = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
entry.target.classList.add('is-visible');
observer.unobserve(entry.target);
}
});
}, { threshold: 0.15, rootMargin: '0px 0px -60px 0px' });
// Stagger siblings within the same parent (e.g. the 4 feature cards, or the
// 4 trust badges) rather than indexing across the whole page.
var seenPerParent = new Map();
targets.forEach(function (el) {
var parent = el.parentElement;
var localIndex = seenPerParent.get(parent) || 0;
seenPerParent.set(parent, localIndex + 1);
el.style.setProperty('--reveal-delay', ((localIndex % 4) * 90) + 'ms');
observer.observe(el);
});
});
})();
+49 -12
View File
@@ -2,6 +2,18 @@
@section('content') @section('content')
<section class="hero"> <section class="hero">
<div class="hero-orbs" aria-hidden="true">
<span class="orb orb-1"></span>
<span class="orb orb-2"></span>
<span class="orb orb-3"></span>
</div>
<div class="hero-float-icon hero-float-icon-1" aria-hidden="true"><i class="fa-solid fa-rss"></i></div>
<div class="hero-float-icon hero-float-icon-2" aria-hidden="true"><i class="fa-solid fa-bell"></i></div>
<div class="hero-float-icon hero-float-icon-3" aria-hidden="true"><i class="fa-solid fa-book-open"></i></div>
<div class="hero-float-icon hero-float-icon-4" aria-hidden="true"><i class="fa-solid fa-house-laptop"></i></div>
<div class="hero-grid">
<div class="hero-inner"> <div class="hero-inner">
<span class="eyebrow">{{ __('app.hero_eyebrow') }}</span> <span class="eyebrow">{{ __('app.hero_eyebrow') }}</span>
<h1>{{ __('app.hero_title') }}</h1> <h1>{{ __('app.hero_title') }}</h1>
@@ -14,34 +26,54 @@
@endif @endif
<a href="{{ route('self-hosting') }}" class="btn btn-ghost btn-lg">{{ __('app.how_to_self_host') }}</a> <a href="{{ route('self-hosting') }}" class="btn btn-ghost btn-lg">{{ __('app.how_to_self_host') }}</a>
</div> </div>
<div class="trust-badges">
<span class="trust-badge reveal"><i class="fa-solid fa-server"></i> {{ __('app.badge_self_hosted') }}</span>
<span class="trust-badge reveal"><i class="fa-brands fa-git-alt"></i> {{ __('app.badge_open_source') }}</span>
<span class="trust-badge reveal"><i class="fa-solid fa-scale-balanced"></i> {{ __('app.badge_mit_license') }}</span>
<span class="trust-badge reveal"><i class="fa-solid fa-language"></i> {{ __('app.badge_four_languages') }}</span>
</div>
</div>
<div class="hero-visual">
<div class="browser-frame">
<div class="browser-frame-bar">
<span class="browser-dot browser-dot-red"></span>
<span class="browser-dot browser-dot-yellow"></span>
<span class="browser-dot browser-dot-green"></span>
</div>
<img src="{{ asset('images/screenshots/dashboard-light.png') }}" alt="BloomFeed dashboard" class="browser-frame-img browser-frame-img-light">
<img src="{{ asset('images/screenshots/dashboard-dark.png') }}" alt="BloomFeed dashboard" class="browser-frame-img browser-frame-img-dark">
</div>
</div>
</div> </div>
</section> </section>
<section class="feature-grid"> <section class="feature-grid">
<div class="feature-card"> <div class="feature-card reveal">
<div class="feature-icon">📰</div> <div class="feature-icon feature-icon-1"><i class="fa-solid fa-rss"></i></div>
<h3>{{ __('app.feature_feeds_title') }}</h3> <h3>{{ __('app.feature_feeds_title') }}</h3>
<p>{{ __('app.feature_feeds_text') }}</p> <p>{{ __('app.feature_feeds_text') }}</p>
</div> </div>
<div class="feature-card"> <div class="feature-card reveal">
<div class="feature-icon">📖</div> <div class="feature-icon feature-icon-2"><i class="fa-solid fa-book-open"></i></div>
<h3>{{ __('app.feature_reader_title') }}</h3> <h3>{{ __('app.feature_reader_title') }}</h3>
<p>{{ __('app.feature_reader_text') }}</p> <p>{{ __('app.feature_reader_text') }}</p>
</div> </div>
<div class="feature-card"> <div class="feature-card reveal">
<div class="feature-icon">🔒</div> <div class="feature-icon feature-icon-3"><i class="fa-solid fa-house-lock"></i></div>
<h3>{{ __('app.feature_home_title') }}</h3> <h3>{{ __('app.feature_home_title') }}</h3>
<p>{{ __('app.feature_home_text') }}</p> <p>{{ __('app.feature_home_text') }}</p>
</div> </div>
<div class="feature-card"> <div class="feature-card reveal">
<div class="feature-icon">🔔</div> <div class="feature-icon feature-icon-4"><i class="fa-solid fa-bell"></i></div>
<h3>{{ __('app.notifiers') }}</h3> <h3>{{ __('app.notifiers') }}</h3>
<p>{{ __('app.notifiers_lede') }}</p> <p>{{ __('app.notifiers_lede') }}</p>
</div> </div>
</section> </section>
<section class="screenshots-section"> <section class="screenshots-section">
<div class="screenshots-header"> <div class="screenshots-header reveal">
<h2>{{ __('app.screenshots_title') }}</h2> <h2>{{ __('app.screenshots_title') }}</h2>
<p>{{ __('app.screenshots_lede') }}</p> <p>{{ __('app.screenshots_lede') }}</p>
</div> </div>
@@ -56,9 +88,14 @@
]; ];
@endphp @endphp
<div class="screenshot-gallery"> <div class="screenshot-gallery reveal">
<div class="screenshot-main"> <div class="browser-frame screenshot-main">
<img id="screenshotMainImg" src="{{ asset('images/screenshots/' . $screenshots[0]['file']) }}" alt="{{ $screenshots[0]['caption'] }}"> <div class="browser-frame-bar">
<span class="browser-dot browser-dot-red"></span>
<span class="browser-dot browser-dot-yellow"></span>
<span class="browser-dot browser-dot-green"></span>
</div>
<img id="screenshotMainImg" class="browser-frame-img" src="{{ asset('images/screenshots/' . $screenshots[0]['file']) }}" alt="{{ $screenshots[0]['caption'] }}">
</div> </div>
<div class="screenshot-thumbs"> <div class="screenshot-thumbs">
@foreach ($screenshots as $index => $shot) @foreach ($screenshots as $index => $shot)
@@ -10,6 +10,7 @@
<link rel="apple-touch-icon" href="{{ asset('images/icon.png') }}"> <link rel="apple-touch-icon" href="{{ asset('images/icon.png') }}">
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css">
<link rel="stylesheet" href="{{ asset('css/theme.css') }}"> <link rel="stylesheet" href="{{ asset('css/theme.css') }}">
<link rel="stylesheet" href="{{ asset('css/app.css') }}"> <link rel="stylesheet" href="{{ asset('css/app.css') }}">
<script> <script>
@@ -48,5 +49,6 @@
</footer> </footer>
<script src="{{ asset('js/theme-toggle.js') }}" defer></script> <script src="{{ asset('js/theme-toggle.js') }}" defer></script>
<script src="{{ asset('js/scroll-reveal.js') }}" defer></script>
</body> </body>
</html> </html>