Hallo @Don, ich habe eine kleine persönliche Anpassung am FKB Pro-Theme vorgenommen, um einen schwebenden Chat-Button hinzuzufügen: auf Tablet und Desktop unten rechts und auf dem Handy unten links, mit demselben Effekt wie beim Button „Thema erstellen“. Ich denke, im Code ginge es besser, aber so funktioniert es: ![]()
Hier ist mein Komponenten-Code: css
// 1. HAUPTSCHWEBENDER BUTTON
.header-dropdown-toggle.chat-header-icon {
position: fixed !important;
bottom: 60px !important;
right: 20px !important;
z-index: 9999 !important;
/* Perfekte Maße (Kreis) */
width: 60px !important;
height: 60px !important;
min-width: 60px !important;
min-height: 60px !important;
border-radius: 50% !important;
/* Leuchtendes Blau und farbiger Schatten */
background: #0084ff !important;
box-shadow: 0 4px 14px rgba(0, 132, 255, 0.4), 0 2px 4px rgba(0, 0, 0, 0.1) !important;
/* Aufräumen der Abstände */
display: flex !important;
align-items: center !important;
justify-content: center !important;
padding: 0 !important;
margin: 0 !important;
border: none !important;
/* Fließender Übergang */
transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275), opacity 0.3s ease, box-shadow 0.2s ease !important;
}
/* Spezifische Position auf Mobile (Bildschirme <= 640px) */
@media (max-width: 640px) {
.header-dropdown-toggle.chat-header-icon {
bottom: 80px !important;
left: 20px !important; /* Ändere zu right: 20px, wenn du es lieber rechts haben möchtest */
}
}
/* Hover-Effekt (leichte Vergrößerung) */
.header-dropdown-toggle.chat-header-icon:hover {
transform: scale(1.08) !important;
box-shadow: 0 6px 20px rgba(0, 132, 255, 0.5), 0 4px 8px rgba(0, 0, 0, 0.15) !important;
}
/* 2. AUFRÄUMEN DES NATIVEN GRÜNEN HINTERGRUNDS VON DISCOURSE */
.header-dropdown-toggle.chat-header-icon .icon,
.header-dropdown-toggle.chat-header-icon span:not(.badge-notification):not(.chat-badge):not([class*="unread"]):not([class*="count"]) {
background-color: transparent !important;
box-shadow: none !important;
border: none !important;
}
/* 3. PERFEKTES ZENTRIEREN DES ICONS (Die Blase) */
.header-dropdown-toggle.chat-header-icon .d-icon,
.header-dropdown-toggle.chat-header-icon svg {
position: absolute !important;
top: 50% !important;
left: 50% !important;
transform: translate(-50%, -50%) !important;
margin: 0 !important;
padding: 0 !important;
width: 32px !important;
height: 32px !important;
color: #ffffff !important;
filter: drop-shadow(0 2px 2px rgba(0, 0, 0, 0.1)) !important;
}
/* 4. BENACHRICHTIGUNGS-BADGE (Grüner Zähler + sichtbare Zahl) */
.header-dropdown-toggle.chat-header-icon .badge-notification,
.header-dropdown-toggle.chat-header-icon .chat-badge,
.header-dropdown-toggle.chat-header-icon .unread-notifications,
.header-dropdown-toggle.chat-header-icon [class*="badge"],
.d-header-icons .header-dropdown-toggle.chat-header-icon .badge-notification {
position: absolute !important;
top: -4px !important;
right: -4px !important;
left: auto !important;
margin: 0 !important;
z-index: 10 !important;
/* Erzwungene grüne Farbe */
background-color: #53A548 !important;
background: #53A548 !important;
color: #ffffff !important;
/* Erzwungene Typografie, um die Zahl sichtbar zu machen */
font-size: 13px !important;
font-weight: bold !important;
line-height: 18px !important;
letter-spacing: normal !important;
text-indent: 0 !important;
/* Maße und Form */
padding: 0 6px !important;
min-width: 22px !important;
height: 22px !important;
border-radius: 50px !important;
border: 2px solid #ffffff !important;
box-sizing: border-box !important;
/* Zentrieren der Zahl */
display: flex !important;
align-items: center !important;
justify-content: center !important;
white-space: nowrap !important;
overflow: visible !important;
/* Grüne Puls-Animation (verknüpft mit deinen Keyframes) */
animation: badge-pulse 2s infinite !important;
}
/* Erzwingt, dass alle Kind-Elemente (falls Discourse die Zahl in ein span einbettet) angezeigt werden */
.header-dropdown-toggle.chat-header-icon .badge-notification *,
.header-dropdown-toggle.chat-header-icon .chat-badge *,
.header-dropdown-toggle.chat-header-icon [class*="badge"] * {
color: #ffffff !important;
background: transparent !important;
display: inline-block !important;
visibility: visible !important;
opacity: 1 !important;
font-size: inherit !important;
}
/* 5. KORRIGIERTE ANIMATIONEN (Radar-Effekt & Bounce) */
/* 5.1 - Die Radarwelle (Puls) um das rote Badge */
.header-dropdown-toggle.chat-header-icon .badge-notification,
.header-dropdown-toggle.chat-header-icon .chat-badge,
.header-dropdown-toggle.chat-header-icon .chat-channel-unread-indicator {
animation: badge-pulse 2s infinite !important;
transform-origin: center !important;
}
/* 5.2 - Bounce-Effekt auf den gesamten blauen Button, wenn eine Nachricht vorliegt */
.header-dropdown-toggle.chat-header-icon:has(.badge-notification),
.header-dropdown-toggle.chat-header-icon:has(.chat-badge),
.header-dropdown-toggle.chat-header-icon:has(.chat-channel-unread-indicator) {
animation: chat-bounce 3s infinite !important;
}
/* --- DEFINITION DER KEYFRAMES (Nicht ändern) --- */
/* Definition der Welle: Wir nutzen nur box-shadow, um Darstellungsfehler zu vermeiden */
@keyframes badge-pulse {
0% {
box-shadow: 0 0 0 0 rgba(250, 62, 62, 0.8);
}
70% {
box-shadow: 0 0 0 10px rgba(250, 62, 62, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(250, 62, 62, 0);
}
}
/* Definition des Bounce: Der Button hüpft alle 3 Sekunden leicht */
@keyframes chat-bounce {
0%, 20%, 50%, 80%, 100% {
transform: translateY(0);
}
40% {
transform: translateY(-6px);
}
60% {
transform: translateY(-3px);
}
}
/* 6. PULSATION IN DER KANAL- UND PRIVATNACHRICHTENLISTE */
/* 6.1 - Überlauf für Listencontainer erlauben */
.chat-channel-row,
.chat-channel-list-item,
.chat-drawer-container .chat-channel-unread-indicator,
.chat-channel-row .chat-channel-unread-indicator {
overflow: visible !important;
}
/* 6.2 - Stil und Abstände des Indikator-Punkts */
.chat-channel-row .chat-channel-unread-indicator,
.chat-channel-row .badge-notification,
.chat-channel-row .unread-indicator,
.chat-drawer-container .chat-channel-unread-indicator,
.chat-full-page .chat-channel-unread-indicator,
.chat-channel-list-item .chat-channel-unread-indicator {
animation: list-dot-pulse 1.8s infinite !important;
border-radius: 50% !important;
position: relative !important;
overflow: visible !important;
margin: 0 6px !important; /* Abstand, damit es nicht an Text und Datum klebt */
}
/* 6.3 - Angepasste Animation (kompaktere Welle, max. 6px) */
@keyframes list-dot-pulse {
0% {
box-shadow: 0 0 0 0 rgba(0, 132, 255, 0.7); /* Passt sich der Farbe des Punkts an */
}
70% {
box-shadow: 0 0 0 6px rgba(0, 132, 255, 0); /* Reduzierter Radius, um in der Zeile zu bleiben */
}
100% {
box-shadow: 0 0 0 0 rgba(0, 132, 255, 0);
}
}
und das JS:
import { apiInitializer } from "discourse/lib/api";
export default apiInitializer((api) => {
let lastScrollTop = 0;
let isHiddenByScroll = false;
// 1. Scroll-Handler (NUR auf Mobile aktiv)
window.addEventListener("scroll", () => {
const isMobile = window.innerWidth <= 640;
const floatBtn = document.querySelector('.header-dropdown-toggle.chat-header-icon');
if (!floatBtn) return;
// Wenn wir auf einem Desktop sind, stellen wir sicher, dass der Button immer sichtbar bleibt
if (!isMobile) {
if (isHiddenByScroll) {
floatBtn.style.setProperty('transform', 'translateY(0) scale(1)', 'important');
floatBtn.style.setProperty('opacity', '1', 'important');
isHiddenByScroll = false;
}
return;
}
const currentScrollTop = window.pageYOffset || document.documentElement.scrollTop;
// Wenn wir nach unten scrollen (und 100px überschritten haben)
if (currentScrollTop > lastScrollTop && currentScrollTop > 100) {
if (!isHiddenByScroll) {
floatBtn.style.setProperty('transform', 'translateY(100px) scale(0.8)', 'important');
floatBtn.style.setProperty('opacity', '0', 'important');
isHiddenByScroll = true;
}
}
// Wenn wir nach oben scrollen
else if (currentScrollTop < lastScrollTop) {
if (isHiddenByScroll) {
floatBtn.style.setProperty('transform', 'translateY(0) scale(1)', 'important');
floatBtn.style.setProperty('opacity', '1', 'important');
isHiddenByScroll = false;
}
}
lastScrollTop = currentScrollTop <= 0 ? 0 : currentScrollTop;
}, { passive: true });
// 2. MutationObserver zum Verwalten der Basis-Anzeige
const observer = new MutationObserver(() => {
const chatOpen = document.querySelector('.chat-drawer, .d-chat-floating-panel, .chat-widget, .chat-panel, .chat-out-of-container, .chat-full-page');
const isChatRoute = window.location.pathname.startsWith('/chat');
const hasFullPageClass = document.body.classList.contains('has-full-page-chat');
const isMobile = window.innerWidth <= 640;
const isTopicRoute = window.location.pathname.includes('/t/') || document.body.classList.contains('archetype-regular');
const hideOnMobileTopic = isMobile && isTopicRoute;
const floatBtn = document.querySelector('.header-dropdown-toggle.chat-header-icon');
if (floatBtn) {
if (chatOpen || isChatRoute || hasFullPageClass || hideOnMobileTopic) {
floatBtn.style.setProperty('display', 'none', 'important');
} else {
floatBtn.style.setProperty('display', 'flex', 'important');
// Saubere Verwaltung der Mobile-/Desktop-Position
if (isMobile) {
floatBtn.style.setProperty('left', '10px', 'important'); // Wert ändern, wenn du es weiter verschieben möchtest
floatBtn.style.removeProperty('right');
} else {
floatBtn.style.setProperty('right', '20px', 'important');
floatBtn.style.removeProperty('left');
}
}
}
});
observer.observe(document.body, { childList: true, subtree: true, attributes: true });
});

