Overlays overlaysfeedbackfloatingwidgetsurvey
Floating Feedback Widget
Fixed bottom-right collapsible feedback trigger with emoji satisfaction ratings and text submission textarea.
Preview
Send Feedback
Usage
Copy the full block below to use this component with its imports.
astro
---
import { FloatingFeedbackWidget } from 'astro-component-kit';
---
<FloatingFeedbackWidget placeholder="How can we improve this experience?" /> ---
import { FloatingFeedbackWidget } from 'astro-component-kit';
---
<FloatingFeedbackWidget placeholder="How can we improve this experience?" /> Manual Installation
If you are not using the npm package, create a new file src/components/lib/FloatingFeedbackWidget.astro and paste the following code:
astro
---
/**
* FloatingFeedbackWidget — Fixed corner floating pill that expands into an interactive feedback survey card.
*
* @param {string} title - Widget title. Default is 'Was this helpful?'.
* @param {string} position - Screen anchor ('bottom-right' | 'bottom-left'). Default is 'bottom-right'.
*/
interface Props {
title?: string;
position?: 'bottom-right' | 'bottom-left';
}
const {
title = 'Send Feedback',
position = 'bottom-right',
} = Astro.props;
---
<div class:list={['fb-widget', `fb-widget--${position}`]} data-feedback-widget>
<button type="button" class="fb-widget__trigger" aria-label="Open feedback modal" data-fb-trigger>
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
</svg>
<span>Feedback</span>
</button>
<div class="fb-widget__card" data-fb-card hidden>
<div class="fb-widget__head">
<h5 class="fb-widget__title">{title}</h5>
<button type="button" class="fb-widget__close" aria-label="Close" data-fb-close>×</button>
</div>
<div class="fb-widget__emojis">
<button type="button" class="emoji-btn" aria-label="Sad">🙁</button>
<button type="button" class="emoji-btn" aria-label="Neutral">😐</button>
<button type="button" class="emoji-btn emoji-btn--selected" aria-label="Happy">😊</button>
<button type="button" class="emoji-btn" aria-label="Love it">🤩</button>
</div>
<textarea
placeholder="Any details to share? (optional)"
class="fb-widget__textarea"
rows="2"
></textarea>
<button type="button" class="fb-widget__send" data-fb-send>
Send Feedback
</button>
</div>
</div>
<style>
.fb-widget {
position: fixed;
z-index: 90;
font-family: var(--font-sans, system-ui, sans-serif);
}
.fb-widget--bottom-right { bottom: 1.5rem; right: 1.5rem; }
.fb-widget--bottom-left { bottom: 1.5rem; left: 1.5rem; }
.fb-widget__trigger {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 0.6rem 1.1rem;
background: rgba(14, 14, 18, 0.9);
backdrop-filter: blur(16px);
border: 1px solid rgba(255, 255, 255, 0.12);
border-radius: var(--r-full, 9999px);
color: var(--c-text-1, #ffffff);
font-size: 0.82rem;
font-weight: 700;
cursor: pointer;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
transition: transform 0.2s ease, border-color 0.2s ease;
}
.fb-widget__trigger:hover {
transform: translateY(-2px);
border-color: rgba(184, 134, 11, 0.4);
color: var(--c-primary-light, #d4a017);
}
.fb-widget__card {
position: absolute;
bottom: calc(100% + 12px);
right: 0;
width: 280px;
padding: 1.2rem;
background: rgba(12, 12, 16, 0.95);
backdrop-filter: blur(24px);
border: 1px solid rgba(255, 255, 255, 0.12);
border-radius: var(--r-xl, 20px);
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
display: flex;
flex-direction: column;
gap: 12px;
}
.fb-widget__head {
display: flex;
justify-content: space-between;
align-items: center;
}
.fb-widget__title {
font-size: 0.88rem;
font-weight: 800;
color: var(--c-text-1, #ffffff);
margin: 0;
}
.fb-widget__close {
background: transparent;
border: none;
color: var(--c-text-3, #737373);
font-size: 1.2rem;
line-height: 1;
cursor: pointer;
}
.fb-widget__emojis {
display: flex;
justify-content: space-between;
gap: 6px;
}
.emoji-btn {
flex: 1;
font-size: 1.4rem;
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: var(--r-md, 10px);
padding: 6px 0;
cursor: pointer;
transition: transform 0.2s ease, background 0.2s ease;
}
.emoji-btn:hover,
.emoji-btn--selected {
transform: scale(1.15);
background: rgba(184, 134, 11, 0.2);
border-color: rgba(184, 134, 11, 0.4);
}
.fb-widget__textarea {
width: 100%;
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: var(--r-sm, 8px);
color: var(--c-text-1, #ffffff);
font-size: 0.8rem;
padding: 8px;
resize: none;
outline: none;
font-family: inherit;
}
.fb-widget__send {
padding: 0.6rem;
background: linear-gradient(135deg, #d4a017, #b8860b);
border: none;
border-radius: var(--r-sm, 8px);
color: #050505;
font-size: 0.8rem;
font-weight: 800;
cursor: pointer;
transition: opacity 0.2s;
}
.fb-widget__send:hover {
opacity: 0.9;
}
</style>
<script>
function setupFeedbackWidgets() {
document.querySelectorAll('[data-feedback-widget]').forEach((widget) => {
const trigger = widget.querySelector('[data-fb-trigger]');
const card = widget.querySelector('[data-fb-card]') as HTMLElement;
const close = widget.querySelector('[data-fb-close]');
const send = widget.querySelector('[data-fb-send]');
if (!trigger || !card) return;
trigger.addEventListener('click', () => {
card.hidden = !card.hidden;
});
close?.addEventListener('click', () => {
card.hidden = true;
});
send?.addEventListener('click', () => {
card.innerHTML = `<p style="text-align:center;font-size:0.85rem;color:#10b981;font-weight:700;margin:1rem 0;">✓ Thank you for your feedback!</p>`;
setTimeout(() => { card.hidden = true; }, 2000);
});
});
}
setupFeedbackWidgets();
document.addEventListener('astro:page-load', setupFeedbackWidgets);
</script> ---
/**
* FloatingFeedbackWidget — Fixed corner floating pill that expands into an interactive feedback survey card.
*
* @param {string} title - Widget title. Default is 'Was this helpful?'.
* @param {string} position - Screen anchor ('bottom-right' | 'bottom-left'). Default is 'bottom-right'.
*/
interface Props {
title?: string;
position?: 'bottom-right' | 'bottom-left';
}
const {
title = 'Send Feedback',
position = 'bottom-right',
} = Astro.props;
---
<div class:list={['fb-widget', `fb-widget--${position}`]} data-feedback-widget>
<button type="button" class="fb-widget__trigger" aria-label="Open feedback modal" data-fb-trigger>
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
</svg>
<span>Feedback</span>
</button>
<div class="fb-widget__card" data-fb-card hidden>
<div class="fb-widget__head">
<h5 class="fb-widget__title">{title}</h5>
<button type="button" class="fb-widget__close" aria-label="Close" data-fb-close>×</button>
</div>
<div class="fb-widget__emojis">
<button type="button" class="emoji-btn" aria-label="Sad">🙁</button>
<button type="button" class="emoji-btn" aria-label="Neutral">😐</button>
<button type="button" class="emoji-btn emoji-btn--selected" aria-label="Happy">😊</button>
<button type="button" class="emoji-btn" aria-label="Love it">🤩</button>
</div>
<textarea
placeholder="Any details to share? (optional)"
class="fb-widget__textarea"
rows="2"
></textarea>
<button type="button" class="fb-widget__send" data-fb-send>
Send Feedback
</button>
</div>
</div>
<style>
.fb-widget {
position: fixed;
z-index: 90;
font-family: var(--font-sans, system-ui, sans-serif);
}
.fb-widget--bottom-right { bottom: 1.5rem; right: 1.5rem; }
.fb-widget--bottom-left { bottom: 1.5rem; left: 1.5rem; }
.fb-widget__trigger {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 0.6rem 1.1rem;
background: rgba(14, 14, 18, 0.9);
backdrop-filter: blur(16px);
border: 1px solid rgba(255, 255, 255, 0.12);
border-radius: var(--r-full, 9999px);
color: var(--c-text-1, #ffffff);
font-size: 0.82rem;
font-weight: 700;
cursor: pointer;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
transition: transform 0.2s ease, border-color 0.2s ease;
}
.fb-widget__trigger:hover {
transform: translateY(-2px);
border-color: rgba(184, 134, 11, 0.4);
color: var(--c-primary-light, #d4a017);
}
.fb-widget__card {
position: absolute;
bottom: calc(100% + 12px);
right: 0;
width: 280px;
padding: 1.2rem;
background: rgba(12, 12, 16, 0.95);
backdrop-filter: blur(24px);
border: 1px solid rgba(255, 255, 255, 0.12);
border-radius: var(--r-xl, 20px);
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
display: flex;
flex-direction: column;
gap: 12px;
}
.fb-widget__head {
display: flex;
justify-content: space-between;
align-items: center;
}
.fb-widget__title {
font-size: 0.88rem;
font-weight: 800;
color: var(--c-text-1, #ffffff);
margin: 0;
}
.fb-widget__close {
background: transparent;
border: none;
color: var(--c-text-3, #737373);
font-size: 1.2rem;
line-height: 1;
cursor: pointer;
}
.fb-widget__emojis {
display: flex;
justify-content: space-between;
gap: 6px;
}
.emoji-btn {
flex: 1;
font-size: 1.4rem;
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: var(--r-md, 10px);
padding: 6px 0;
cursor: pointer;
transition: transform 0.2s ease, background 0.2s ease;
}
.emoji-btn:hover,
.emoji-btn--selected {
transform: scale(1.15);
background: rgba(184, 134, 11, 0.2);
border-color: rgba(184, 134, 11, 0.4);
}
.fb-widget__textarea {
width: 100%;
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: var(--r-sm, 8px);
color: var(--c-text-1, #ffffff);
font-size: 0.8rem;
padding: 8px;
resize: none;
outline: none;
font-family: inherit;
}
.fb-widget__send {
padding: 0.6rem;
background: linear-gradient(135deg, #d4a017, #b8860b);
border: none;
border-radius: var(--r-sm, 8px);
color: #050505;
font-size: 0.8rem;
font-weight: 800;
cursor: pointer;
transition: opacity 0.2s;
}
.fb-widget__send:hover {
opacity: 0.9;
}
</style>
<script>
function setupFeedbackWidgets() {
document.querySelectorAll('[data-feedback-widget]').forEach((widget) => {
const trigger = widget.querySelector('[data-fb-trigger]');
const card = widget.querySelector('[data-fb-card]') as HTMLElement;
const close = widget.querySelector('[data-fb-close]');
const send = widget.querySelector('[data-fb-send]');
if (!trigger || !card) return;
trigger.addEventListener('click', () => {
card.hidden = !card.hidden;
});
close?.addEventListener('click', () => {
card.hidden = true;
});
send?.addEventListener('click', () => {
card.innerHTML = `<p style="text-align:center;font-size:0.85rem;color:#10b981;font-weight:700;margin:1rem 0;">✓ Thank you for your feedback!</p>`;
setTimeout(() => { card.hidden = true; }, 2000);
});
});
}
setupFeedbackWidgets();
document.addEventListener('astro:page-load', setupFeedbackWidgets);
</script>
Quick Info
- Category
- Overlays
- Filename
FloatingFeedbackWidget.astro- Dependencies
- None — pure Astro + CSS
- Tags
- overlaysfeedbackfloatingwidgetsurvey