Skip to content

Voice Button

Diktat für ein Texteingabefeld, über die Web-Speech-API.

Nimm sie, wenn Sprache eine weitere Art sein soll, ein Textfeld zu füllen — kein Ersatz dafür. Tippen muss verfügbar bleiben: Ein Weg nur über Sprache schließt alle aus, deren Sprechen abweicht, alle in einem lauten Raum und alle, deren Browser gar keine Erkennung mitbringt.

Eine Mikrofon-Schaltfläche, sonst nichts. Sie übernimmt die Aufnahme und meldet, was gehört wurde; wohin dieser Text geht, entscheidet der Aufrufer — denn eine Komponente, die auch in ein Feld schriebe, müsste wissen, in welches, ob anhängen oder ersetzen und was mit der Einfügemarke geschehen soll: drei Antworten, die je nach Anwendung anders ausfallen.

Schnellstart

html
<r-voice-button label="Spracheingabe starten" active-label="Spracheingabe beenden"></r-voice-button>
ts
const mic = document.createElement('r-voice-button');
mic.label = 'Spracheingabe starten';
mic.activeLabel = 'Spracheingabe beenden';
const input = document.querySelector('textarea');
let base = '';

mic.addEventListener('voicestart', () => {
  // Ein Leerzeichen zwischen Getipptem und Gesprochenem, sofern nicht schon eines da ist.
  base = input.value === '' || /\s$/.test(input.value) ? input.value : `${input.value} `;
});

mic.addEventListener('voiceresult', (event) => {
  input.value = base + event.detail.transcript;
});

composer.append(mic);

Die Entscheidungen dahinter

Sie meldet die ganze Aufnahme, nicht das neueste Bruchstück

Zwischenergebnisse werden überarbeitet, während die Erkennung weiterläuft: Aus „你好“ wird „你好世界“ — es kommt kein zweites Event mit „世界“ hinterher. Wer jedes Event anhängte, landete bei 你好你好世界. Merke dir den Text, der schon im Feld stand, und verkette genau einmal.

Sie sendet nicht

Erkennung liegt oft genug daneben, dass ein stellvertretendes Absenden dem Sprecher die Prüfung nimmt, die er braucht. Dies füllt das Feld und hört dort auf. Absenden bleibt eine bewusste Handlung.

Sie versteckt sich, wo es keine Erkennung gibt

Firefox liefert keine Spracherkennung, und ebenso wenig jeder Browser, dem die API fehlt. Wird Erkennung nicht unterstützt, versteckt sich das Element mit hidden, statt sich mit disabled abzuschalten: disabled legt nahe, dass es die Funktion gibt, sie aber vorübergehend nicht verfügbar ist — die Schaltfläche zu entfernen ist dagegen zutreffend, wenn es die Funktion auf dieser Plattform überhaupt nicht gibt. Eine Schaltfläche zu zeigen, die nie funktionieren kann, lüde zu einem Tippen ein, das nichts tut, und verlangte dann eine Erklärung.

Nur zwei von vier Fehlern lohnen die Anzeige

ArtWas es istAnzeigen?
denieddas Mikrofon wurde verweigertja (man kann etwas tun)
failedirgendetwas anderes ging schiefja
noSpeecheine stille Pausenein
abortedein Stopp aus dem Programmnein

Die letzten beiden kommen über denselben Kanal wie ein echter Fehlschlag und sind keiner. Sie anzuzeigen hieße, nach jeder gewöhnlichen Aufnahme einen Fehler zu zeigen, nicht nur bei echten Fehlschlägen.

Barrierefreiheit

Der zugängliche Name ändert sich mit dem Zustand, nicht nur das Icon, und aria-pressed trägt den Umschaltzustand: Ein Screenreader sagt „Spracheingabe beenden, gedrückt“ an, kein Icon. Escape verwirft eine Aufnahme, statt sie zu übernehmen — genau das will jemand, der mitten im Satz merkt, dass er das Falsche gesagt hat.

Der Zuhörzustand wird über Rahmen, Füllung und einen Ring vermittelt, hängt also nicht allein an der Farbe. Der Ring ist die einzige Bewegung und reine Dekoration; prefers-reduced-motion lässt ihn weg, ohne dass Information verloren geht.

Die Sprache folgt der Seite

lang wird pro Aufnahme gelesen und übernimmt standardmäßig die des Dokuments — eine Anwendung, die mitten in der Sitzung die Sprache wechselt, diktiert also in der Sprache, die sie gerade zeigt.

API-Referenz

Eigenschaften

EigenschaftTypStandardBeschreibung
langstringdie des DokumentsBCP-47-Tag der gesprochenen Sprache. Wird pro Aufnahme gelesen.
continuousbooleantrueHört über Pausen hinweg weiter, statt bei der ersten aufzuhören.
disabledbooleanfalseSchaltet die Schaltfläche ab: start() wird ignoriert, die innere Schaltfläche deaktiviert. Eine laufende Aufnahme stoppt es nicht.
labelstring'Start voice input'Zugänglicher Name im Ruhezustand.
activeLabelstring'Stop voice input'Zugänglicher Name beim Zuhören.
listeningbooleanfalseNur lesbar, gespiegelt: gestalte mit :host([listening]).
supportedbooleanNur lesbar. Ob diese Plattform Sprache erkennen kann.
sheetstring''CSS, das in das Shadow DOM des Elements injiziert wird.

Methoden

start() · stop() (behält das Erkannte) · abort() (verwirft es) · toggle().

toggle() liest den Zustand des Erkenners selbst statt des gespiegelten Attributs: Eine Aufnahme, die begonnen hat, ohne es zu melden, ließe beide sonst auseinanderlaufen, und die nächste Aktivierung versuchte eine zweite Aufnahme zu öffnen, würde abgewiesen und täte nichts.

Events

EventDetailWird ausgelöst, wenn
voicestarteine Aufnahme beginnt
voiceresult{ transcript, isFinal }Text eintrifft oder überarbeitet wird
voiceerror{ kind, detail }die Plattform ein Problem meldet
voiceenddie Aufnahme endet, aus welchem Grund auch immer

Parts

button, icon.

Styling

<r-voice-button> stellt 20 eigene CSS-Custom-Properties bereit, dazu die semantischen Tokens, die es aus dem Theme liest. Setze eine dort, wo sie vererbt wird: :root, ein Wrapper oder das Element selbst:

css
r-voice-button {
  --ran-voice-background: var(--ran-color-bg-subtle);
}

Parts: button · hint · icon

Die vollständige Liste steht in den Style-Tokens; welches Token du wählst, klärt das Designsystem.

Siehe auch

Veröffentlicht unter der MIT-Lizenz.