.cfb-voice {
position: relative;
width: 56px; height: 56px;
border: 0; border-radius: 50%;
background: #1a1a28;
border: 1px solid rgba(255,255,255,0.12);
color: #a78bfa;
cursor: pointer;
display: flex; align-items: center; justify-content: center;
box-shadow: 0 4px 14px rgba(0,0,0,0.35);
transition: background 0.2s, border-color 0.2s, color 0.2s;
}
.cfb-voice:hover { background: #1f1f2e; border-color: rgba(167,139,250,0.4); }
.cfb-voice:focus-visible { outline: 2px solid #7c6cff; outline-offset: 3px; }
.cfb-voice svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
.cfb-voice[aria-pressed="true"] {
background: linear-gradient(135deg, #ff6c8a, #ff5d6c);
border-color: transparent;
color: #fff;
}
.cfb-voice[aria-pressed="true"]::before,
.cfb-voice[aria-pressed="true"]::after {
content: '';
position: absolute; inset: 0;
border-radius: 50%;
border: 2px solid rgba(255,108,138,0.6);
animation: cfb-voice-pulse 1.4s ease-out infinite;
}
.cfb-voice[aria-pressed="true"]::after { animation-delay: 0.7s; }
@keyframes cfb-voice-pulse {
0% { transform: scale(1); opacity: 1; }
100% { transform: scale(1.6); opacity: 0; }
} <button
type="button"
class="cfb-voice"
aria-label="Start voice input"
aria-pressed="false"
data-cfb-voice
>
<svg viewBox="0 0 24 24" aria-hidden="true">
<rect x="9" y="3" width="6" height="11" rx="3" />
<path d="M5 11a7 7 0 0 0 14 0M12 18v3" />
</svg>
</button> // Voice FAB — toggle aria-pressed (mock recording state for the demo)
document.querySelectorAll('[data-cfb-voice]').forEach(function (btn) {
btn.addEventListener('click', function () {
var on = btn.getAttribute('aria-pressed') === 'true';
btn.setAttribute('aria-pressed', on ? 'false' : 'true');
btn.setAttribute('aria-label', on ? 'Start voice input' : 'Stop voice input');
// Hook real Web Speech API here:
// const rec = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
});
}); Live preview Edit any tab — preview updates live Ready