InteraktionGilt fürLite und Pro
Tastatur, Barrierefreiheit und Touch
Setzen Sie in Pro keyboardEnabled (standardmäßig aus), ergänzen Sie keyboardMode: 'Full' für den vollständigen Tastensatz und keyboardTarget: 'component', damit Tasten nur wirken, solange das Raster den Fokus hat. Das Raster ist ein einziger Tab-Stopp mit role="grid": Der Fokus wandert über aria-activedescendant, und Änderungen werden in neun Sprachen angesagt. Lite bringt die Navigation mit den Pfeiltasten von Haus aus mit. Auf Touchscreens halten Sie ein Ereignis gedrückt, um es zu verschieben, und ziehen an seinen Griffen, um seine Dauer zu ändern.
Ein Ressourcenplaner ist ein großes zweidimensionales Raster; das macht die Unterstützung von Tastatur und Screenreader schwieriger als bei einer Liste oder einem Formular. SuperScheduler gibt dem Raster einen einzigen Tab-Stopp, einen wandernden Fokus, der die Virtualisierung übersteht, gesprochene Ansagen für Fokus und Änderungen sowie Tastaturentsprechungen zum Verschieben von Ereignissen und Ändern ihrer Dauer. Dieser Leitfaden erklärt, was jede Edition leistet, wie Sie es aktivieren und was Ihre Anwendung noch selbst bereitstellen muss.
Lite und Pro im Überblick
Lite (super-scheduler-lite) | Pro (super-scheduler) | |
|---|---|---|
| Tastatur | Immer aktiv: Pfeiltasten bewegen die aktive Zelle, Eingabe oder Leertaste aktiviert sie | Standardmäßig aus; keyboardEnabled, dazu keyboardMode: 'Full' für das vollständige Modell |
| Ereignisse | Native Buttons: Tab erreicht sie, Eingabe oder Leertaste klickt sie | Mit den Pfeiltasten innerhalb des Rasters erreichbar; Eingabe startet den Klickablauf |
| Bearbeiten per Tastatur | Nein (schreibgeschützte Edition) | Verschieben mit Alt + Pfeiltasten, Dauer ändern mit Alt + Umschalt + Links/Rechts (Modus Full) |
| Ansagen | Nein | Fokus, Auswahl und übernommene Änderungen, in neun Sprachen |
| Name des Rasters | Option ariaLabel (Standard „Resource schedule“) | Eingebaut „Scheduler“ („Planificador“ bei spanischen Locales) |
| Touch | Natives Scrollen und Tippen | Gedrückt halten zum Verschieben, Griffe zum Ändern der Dauer, Pinch-Geste zum Zoomen |
Die Tastatur in Pro aktivieren
Pro lässt die Tastatur ausgeschaltet, bis Sie keyboardEnabled: true setzen. Der Standard keyboardMode: 'SuperScheduler' verarbeitet Pfeiltasten, Eingabe und Umschalt + Links/Rechts. keyboardMode: 'Full' ergänzt den Rest des Modells: Pos1/Ende, Bild auf/Bild ab, Leertaste, Verschieben und Dauer ändern von Ereignissen, die Menütaste und die Ansage von Hilfetexten. Der Modus Full ohne keyboardEnabled bewirkt nichts und warnt in der Entwicklung.
keyboardTarget bestimmt, wo Tasten gehört werden. Der Standard, 'document', reagiert auf Tasten, die irgendwo auf der Seite außerhalb von Textfeldern gedrückt werden, und nimmt damit dem Scrollen der Seite die Pfeiltasten weg. Verwenden Sie 'component', damit Tasten nur wirken, solange das Raster den Fokus hat; das ist auch die richtige Wahl bei mehreren Planern auf einer Seite.
import { useMemo } from 'react'
import { SuperSchedulerComponent } from 'super-scheduler'
import type { SchedulerProps, SuperScheduler } from 'super-scheduler'
import 'super-scheduler/styles.css'
// Module-level: the same object on every render.
const KEYBOARD: SchedulerProps = {
keyboardEnabled: true,
// Keys act only while the grid has focus; the page keeps its own arrow-key scrolling.
keyboardTarget: 'component',
keyboardMode: 'Full',
keyboardOptions: { pageRows: 10, zoomKeys: true },
}
const UNDER_MAINTENANCE = new Set<SuperScheduler.ResourceId>(['room-104'])
export function AccessiblePlanner(props: {
resources: SuperScheduler.ResourceData[]
events: SuperScheduler.EventData[]
onOpen: (id: SuperScheduler.EventId) => void
}) {
const { onOpen } = props
const events = useMemo(() => props.events.slice(), [props.events])
const config = useMemo<SchedulerProps>(
() => ({
...KEYBOARD,
// Enter on a focused event runs the same click flow as the pointer.
onEventClick: (args) => onOpen(args.e.id()),
// Alt + arrow moves go through the same rules as drags.
onEventMoving: (args) => {
if (UNDER_MAINTENANCE.has(args.resource)) {
args.allowed = false
args.message = 'Room under maintenance'
}
},
}),
[onOpen],
)
return (
// The grid's own accessible name is generic: label the region around it.
<section aria-labelledby="room-plan-title">
<h2 id="room-plan-title">Room plan, October 2026</h2>
<SuperSchedulerComponent
{...config}
startDate="2026-10-01"
days={31}
scale="Day"
locale="en-us"
resources={props.resources}
events={events}
/>
</section>
)
}Sie sollten mit Tab in das Raster gelangen, sich mit den Pfeiltasten bewegen, ein Ereignis mit der Eingabetaste öffnen und mit Alt + Pfeil nach unten das Verschieben eines Ereignisses starten können. Verschieben Sie es auf Zimmer 104 und drücken Sie die Eingabetaste, wird „Not allowed here“ angesagt, und das Ereignis bleibt, wo es war.
keyboardOptions stimmt den Modus Full ab:
| Option | Standard | Wirkung |
|---|---|---|
pageRows | sichtbare Zeilen minus eins | Zeilen, um die Bild auf und Bild ab springen |
contextMenuKey | true im Modus Full | Menütaste und Umschalt + F10 öffnen das Menü des fokussierten Elements |
bubbleOnFocus | false | Zeigt die Blase des Ereignisses, solange es den Tastaturfokus hat |
selectAll | true im Modus Full | Strg/Cmd + A wählt jedes sichtbare Ereignis aus (erfordert allowMultiSelect) |
zoomKeys | false | Strg/Cmd mit = oder +, - und 0 zoomen hinein, heraus und zurück |
zoomKeys ist standardmäßig aus, damit die browsereigenen Tastenkürzel für den Seitenzoom weiter funktionieren.
Tasten
| Tasten | Modus | Was passiert |
|---|---|---|
| Pfeiltasten | beide | Bewegen den Fokus. Links und Rechts halten an jedem Ereignis und jeder leeren Zelle der Zeile; Hoch und Runter wechseln die Zeile. |
| Eingabe | beide | Auf einem Ereignis: der Klickablauf (onEventClick, dann eventClickHandling). Auf einer Zelle: wählt sie als Zeitraum aus. |
| Umschalt + Links / Umschalt + Rechts | beide | Erweitert einen Zeitraum ab der fokussierten Zelle; Loslassen der Umschalttaste wählt ihn aus. |
| Leertaste | Full | Auf einer Zelle: fügt sie der Auswahl hinzu oder entfernt sie daraus. Auf einem Ereignis: wie Eingabe. |
| Pos1 / Ende | Full | Erste oder letzte Zelle der Zeile; mit Strg/Cmd die erste oder letzte Zeile. |
| Bild auf / Bild ab | Full | Bewegt den Fokus um pageRows. |
| Alt + Pfeiltasten | Full | Startet das Verschieben des fokussierten Ereignisses; die Pfeiltasten bewegen es, Eingabe oder Leertaste legt es ab. |
| Alt + Umschalt + Links / Rechts | Full | Startet das Ändern des Endes des fokussierten Ereignisses; Links und Rechts ändern es, Eingabe bestätigt. Auf einem Spaltentitel: verschiebt die Spalte. |
| Esc | beide | Bricht ein Verschieben, Dauer ändern oder Auswählen eines Zeitraums per Tastatur ab. Während eines Verschiebens oder Dauer-Änderns per Tastatur bricht auch Tab ab und verlässt das Raster. |
| Menütaste, Umschalt + F10 | Full | Öffnet das Menü des fokussierten Ereignisses, Zeilenkopfs oder der fokussierten Zelle. |
| Strg/Cmd + A | Full | Wählt jedes sichtbare Ereignis aus. |
Fokusmodell und Screenreader
Das Pro-Raster hat role="grid" mit aria-rowcount und aria-colcount; Zeilenköpfe sind rowheader, Zellen des Zeitkopfs columnheader, und Zellen und Ereignisse gridcell. Zeilen und Zellen außerhalb des Viewports sind nicht im DOM, daher wandert der Fokus nicht von Element zu Element. Stattdessen gilt:
- Die Wurzel des Rasters ist der einzige Tab-Stopp (
tabindex="0"), solange die Tastatur aktiviert ist. - Die fokussierte Zelle oder das fokussierte Ereignis ist ein Fokusknoten, auf den die Wurzel mit
aria-activedescendantverweist; er übersteht Scrollen und Virtualisierung. - Die Beschriftung des Fokusknotens lautet etwa „Room 101, Oct 1“ für eine Zelle und „Ana, Room 101, Oct 2 – 4“ für ein Ereignis.
Der Name eines Ereignisses stammt aus seinem ariaLabel, dann aus seinem text, dann aus seinem html als reiner Text, dann aus seiner ID. Der Name einer Zeile ist der name ihrer Ressource. Wenn html etwas anderes zeigt als text, setzen Sie ariaLabel in onBeforeEventRender:
import { SuperScheduler } from 'super-scheduler'
import type { SchedulerProps } from 'super-scheduler'
type Visit = { patient: string; kind: 'checkup' | 'surgery'; color: string }
const KIND_LABEL: Record<Visit['kind'], string> = { checkup: 'check-up', surgery: 'surgery' }
/** Pass as `onBeforeEventRender` (module-level, so its identity never changes). */
export const labelVisit: NonNullable<SchedulerProps['onBeforeEventRender']> = (args) => {
const visit = args.data as SuperScheduler.EventRenderData<Visit>
args.data.backColor = visit.color
// Dark text on light fills and light text on dark ones (WCAG contrast).
args.data.fontColor = SuperScheduler.ColorUtil.contrasting(visit.color)
// `html` is trusted markup: escape what users typed.
args.data.html = `<strong>${SuperScheduler.Util.escapeHtml(visit.patient)}</strong>`
// The accessible name of the event. Focus labels and announcements append
// the row name and the dates, so they are not repeated here.
args.data.ariaLabel = `${visit.patient}, ${KIND_LABEL[visit.kind]}`
}Da Fokusbeschriftungen und Ansagen die Zeile und die Daten anhängen, sollte ariaLabel nur enthalten, was das Ereignis identifiziert. SuperScheduler.ColorUtil.contrasting(color) liefert dunklen Text für helle Füllungen und hellen Text für dunkle.
Der eigene barrierefreie Name des Rasters ist „Scheduler“ („Planificador“, wenn die Locale mit es beginnt), und 0.1.0 hat keine Option, ihn zu ändern. Setzen Sie den Planer in einen Bereich, der durch eine sichtbare Überschrift benannt ist, wie es das Setup-Snippet tut.
Aus Code bietet control.keyboard die Methoden focusEvent(e or id), focusCell(date, resource), getFocus(), move(direction), clearFocus() und resetFocus(). onKeyboardFocusChange (abbrechbar) und onKeyboardFocusChanged melden Fokuswechsel mit previous und focus ({ e } oder { cell }); damit halten Sie ein Detailpanel mit der Tastatur synchron.
Ansagen
Eine höfliche Live-Region (polite) im Raster sagt an:
- im Modus Full einen kurzen Hilfetext, wenn das Raster zum ersten Mal den Fokus erhält;
- Auswahlen („Selected: Room 101, Oct 4“), aufgehobene Auswahlen und „N events selected“;
- den Beginn eines Verschiebens oder Dauer-Änderns per Tastatur, mit Anweisungen;
- übernommene Verschiebungen und Dauer-Änderungen („Event moved to Room 101, Oct 3 – 5“), egal ob sie von der Tastatur oder vom Zeiger kamen;
- „Cancelled“, „Not allowed here“ und Spaltenverschiebungen.
Die Texte folgen dem ersten Segment der locale des Planers: Englisch, Spanisch, Katalanisch, Baskisch, Galicisch, Deutsch, Französisch, Italienisch und Portugiesisch. Andere Sprachen fallen auf Englisch zurück. Sprachpakete außer Englisch und Spanisch werden bei Bedarf geladen.
Die Menütaste
Im Modus Full öffnen die Menütaste und Umschalt + F10 das Menü des fokussierten Elements, wenn es ein SuperScheduler.Menu ist: das contextMenu des Ereignisses (oder das contextMenu des Controls), contextMenuResource auf einem Zeilenkopf und contextMenuSelection auf einer Zelle. Zeichnet Ihre Anwendung ihr eigenes Menü aus onEventRightClick, verarbeiten Sie die Taste selbst in onKeyDown:
import type { SchedulerProps, SuperScheduler } from 'super-scheduler'
export interface MenuRequest {
readonly eventId: SuperScheduler.EventId
/** Viewport coordinates where the application opens its own menu. */
readonly x: number
readonly y: number
}
/**
* Opens the application's menu from the pointer (right click) and from the keyboard
* (Menu key, Shift+F10). The library's Menu key support covers `SuperScheduler.Menu`
* objects only, so a custom menu handles the key in `onKeyDown`.
*/
export function menuHandlers(open: (request: MenuRequest) => void): SchedulerProps {
return {
onEventRightClick: (args) => {
args.preventDefault()
open({ eventId: args.e.id(), x: args.originalEvent.clientX, y: args.originalEvent.clientY })
},
onKeyDown(args) {
const key = args.originalEvent
if (key.key !== 'ContextMenu' && !(key.key === 'F10' && key.shiftKey)) return
const focused = this.keyboard.getFocus().e
const root = key.target
if (focused === undefined || !(root instanceof HTMLElement)) return
// Skips the library's own handling of the key.
args.preventDefault()
// The grid points at the focused item with aria-activedescendant.
const ring = root.ownerDocument.getElementById(
root.getAttribute('aria-activedescendant') ?? '',
)
const box = (ring ?? root).getBoundingClientRect()
open({ eventId: focused.id(), x: box.left, y: box.bottom })
},
}
}Übergeben Sie menuHandlers(open) memoisiert per Spread an die Props des Planers. Ihr Menü ist dann selbst für seinen Fokus verantwortlich: Verschieben Sie den Fokus beim Öffnen hinein und geben Sie ihn beim Schließen an das Raster zurück.
Barrierefreie Alternativen zum Ziehen
Das WCAG-2.2-Erfolgskriterium 2.5.7 (↗) verlangt eine Möglichkeit, mit einfachen Zeigeraktionen zu erledigen, was Ziehen erledigt. Der vollständige Tastaturmodus deckt Tastaturnutzer ab, aber nicht jemanden, der einen einzelnen Zeiger, einen Schalter oder Sprachsteuerung verwendet. Geben Sie jedem Ereignis einen Weg ohne Ziehen, zum Beispiel ein Detailpanel oder einen Kontextmenüeintrag mit Feldern für Beginn, Ende und Ressource, der Ihren State aktualisiert (oder control.events.update mit einem neuen Objekt aufruft). Führen Sie vor dem Speichern dieselbe Validierung aus, die Sie in onEventMoving verwenden, damit beide Wege dieselben Regeln durchsetzen.
Touch
Auf Touchscreens scrollt ein Finger die Zeitleiste. Der Rest des Touch-Modells:
- Ein Ereignis verschieben. Halten Sie das Ereignis
tapAndHoldTimeoutlang (300 ms) still und ziehen Sie dann. Ein Finger, der sich vorher mehr als etwa 8 px bewegt, scrollt stattdessen.eventTapAndHoldHandlingbestimmt, was das Gedrückthalten bewirkt:'Move'(Standard),'ContextMenu'(öffnet dasSuperScheduler.Menudes Ereignisses) oder'Disabled'. Gedrückthalten auf einem Zeilenkopf öffnetcontextMenuResource. - Dauer ändern. Ein Tippen auf ein Ereignis zeigt seine Griffe mit 44 px großen Touch-Zielen (
--super-scheduler-handle-target); ziehen Sie einen Griff, um die Dauer zu ändern. Der Rand eines Ereignisses unter einem Finger verschiebt es, statt seine Dauer zu ändern. - Zeit auswählen. Ein Tippen auf eine leere Zelle wählt sie aus (
origin: 'click'); Gedrückthalten und Ziehen wählt einen Zeitraum aus. - Zoom. Zwei Finger zoomen per Pinch-Geste (
zoomGesture.pinch, standardmäßig aktiv). Ein zweiter Finger bricht jedes laufende Ziehen ab. - Hover. Touch kennt kein Hover. Hover-Karten aus
eventHoverlassen sich per Tippen anheften (pin: 'click'); Areas mitvisibility: 'TouchVisible'bleiben auf Touch-Geräten sichtbar, während Areas mit'Hover'nicht erscheinen.
Lite ist schreibgeschützt: Es scrollt nativ und meldet Tippen über onEventClick und onTimeRangeClick.
Reduzierte Bewegung, Kontrast und erzwungene Farben
Pro liest die Präferenzen des Nutzers über CSS und Media Queries, ohne dass Sie eine Option setzen müssen:
prefers-reduced-motion: reducesetzt--super-scheduler-durationauf0s, lässtcontrol.zoom.animateTo()ohne Animation springen und entfernt die Übergänge der Hover-Karten;prefers-contrast: moreverstärkt Rahmen, Rasterlinien, Zeilenlinien und die Umrandung der Auswahl;forced-colors: activestellt das Theme auf Systemfarben um (Highlight,CanvasText,GrayText) und verzichtet auf dekorative Schattierungen wie Wochenenden und den heutigen Tag.
Auch Lite passt seine Rahmen und die Fokusumrandung an erzwungene Farben an. Wenn Sie Farben durch eigene Tokens oder eigenes CSS ersetzen, testen Sie diese Modi erneut: Ihre Überschreibungen können sie zunichtemachen.
Tastaturunterstützung in Lite
Lite braucht keine Konfiguration. Das Raster ist fokussierbar, aria-readonly und über die Option ariaLabel benannt. Pfeiltasten bewegen die aktive Zelle (bekanntgegeben über aria-activedescendant), Eingabe oder Leertaste ruft onTimeRangeClick für sie auf, und jedes Ereignis ist ein nativer Button.
import { SuperSchedulerComponent } from 'super-scheduler-lite'
import type { SuperScheduler } from 'super-scheduler-lite'
import 'super-scheduler-lite/styles.css'
const ROOMS: SuperScheduler.ResourceData[] = [
{ id: 'r101', name: 'Room 101' },
{ id: 'r102', name: 'Room 102' },
]
const STAYS: SuperScheduler.EventData[] = [
{ id: 'b1', resource: 'r101', start: '2026-10-02', end: '2026-10-05', text: 'Lena Fischer' },
]
export function OccupancyBoard(props: {
onOpenBooking: (id: SuperScheduler.EventData['id']) => void
onOpenDay: (resource: SuperScheduler.ResourceData['id'], day: string) => void
}) {
return (
<SuperSchedulerComponent
// The grid's accessible name (default "Resource schedule").
ariaLabel="Room occupancy, October 2026"
startDate="2026-10-01"
days={31}
scale="Day"
resources={ROOMS}
events={STAYS}
// Events are native buttons: Tab reaches them, Enter and Space click them.
onEventClick={({ e }) => props.onOpenBooking(e.data.id)}
// Arrow keys move the active cell; Enter or Space activates it.
onTimeRangeClick={({ start, resource }) =>
props.onOpenDay(resource, start.toString('yyyy-MM-dd'))
}
/>
)
}Was Ihre Anwendung noch sicherstellen muss
Die Bibliothek kümmert sich um das Raster. Diese Teile gehören Ihrer Anwendung:
- Kontrast. Farben von Ereignissen, die Sie mit
backColor,fontColor, CSS oder eigenen Inhalten setzen, müssen die Kontrastanforderungen im hellen und dunklen Modus erfüllen. - Namen in eigenen Inhalten. HTML aus
onBeforeEventRender, React-Slots und Markup im Zeilenkopf liegen bei Ihnen: Halten Sietextaussagekräftig oder setzen SieariaLabel, geben Sie Icons Textalternativen und vermeiden Sie interaktive Steuerelemente im Inhalt von Ereignissen. - Menüs, Dialoge und Panels. Fokusverwaltung, Beschriftungen und Esc-Verhalten für alles, was Sie aus dem Raster heraus öffnen.
- Ein Weg ohne Ziehen für jede Ziehaktion, wie oben beschrieben.
- Seitenstruktur. Eine Überschrift oder Beschriftung für den Bereich um das Raster und ein sinnvoller Platz in der Tab-Reihenfolge.
- Tests. Prüfen Sie Ihre Konfiguration mit einem Screenreader und einem automatisierten Prüfwerkzeug; Überschreibungen und eigene Inhalte können ändern, was Nutzer hören.
Siehe auch
- Ressourcenbäume, Spalten und Auswahl behandelt, was Leertaste, Eingabe und Strg/Cmd + A auswählen.
- Ziehen, Dauer ändern und Geschäftsregeln erklärt die Regeln, die Verschiebungen per Tastatur durchlaufen.
- Themes, Tokens, Tailwind und Dark Mode behandelt Fokus- und Auswahlfarben.