> Authoring guidance for interaction-pattern-library.md. Load only the part covering the section currently being authored — not the whole file at once. > Covers: **Standard Control Patterns** — full reference specifications for Button (Primary/Secondary/Destructive), Toggle, Slider, Dropdown / Select, List Item, Grid Item, Modal Dialog, Confirmation Dialog, Toast / Notification, Tooltip, Progress Bar, Input Field, Tab Bar, Scroll Container. ## Standard Control Patterns --- #### Button (Primary) **Category**: Input **Status**: Draft **When to Use**: The single most important action on a screen. "Start Game," "Confirm," "Accept," "Buy." There should be at most one Primary button visible at a time. It is the answer to "what does the player most likely want to do here?" **When NOT to Use**: Alternative or secondary actions; destructive actions that require confirmation before the consequence is irreversible; any action that is not the primary intent of the screen. **Interaction Specification**: | State | Visual | Input | Response | Duration | Audio | |-------|--------|-------|----------|----------|-------| | Default | Full-opacity fill, primary color from art-bible. Label centered. | — | — | — | — | | Hovered (mouse) | Brightness +15%, subtle scale 1.03x, cursor changes to pointer | Mouse over element | Transition from Default | 80ms ease-out | [UI hover sound — see Sound Standards] | | Focused (keyboard/gamepad) | Focus ring visible (2px, offset 3px, high contrast color). Same brightness as Hovered. | Tab / D-pad navigation | Transition from Default | 80ms ease-out | [UI focus sound — same as hover] | | Pressed | Scale 0.97x, brightness -10% | Click / Enter / A (Xbox) / Cross (PS) | Action fires on press-up, not press-down. Scale on press-down. | 60ms ease-in for press; 80ms ease-out on release | [UI confirm sound] | | Disabled | 40% opacity, no pointer cursor, no hover state | — | No response | — | — | | Loading (post-press) | Replace label with spinner. Button remains at pressed scale, disabled state. | — | Prevents double-submission | Duration of async operation | — | **Accessibility**: - Keyboard: Tab to focus, Enter or Space to activate. Must be reachable from any other interactive element on screen via Tab sequence. - Gamepad: D-pad or left stick to navigate focus to button. A (Xbox) / Cross (PS) to activate. Focus must be placed on Primary button by default when screen opens. - Screen reader: Button must expose accessible name matching visible label. Role: "button." State: "dimmed" when disabled. Activation announcement: "[Label] button — [result of action, if known]." - Colorblind: Do not rely on color alone to distinguish Primary from Secondary. Primary uses higher visual weight (fill vs. outline, or larger size) in addition to color differentiation. - Minimum touch target: 44x44pt (iOS HIG) / 48x48dp (Android). Apply even on PC if touch support is possible. **Implementation Notes**: [Godot: Extend `Button` control. Override `_draw()` for custom states rather than modifying themes mid-state. Use `focus_mode = FOCUS_ALL` to ensure keyboard focusability. Set `mouse_default_cursor_shape = CURSOR_POINTING_HAND`. For the scale animation, use a Tween on the `scale` property of the button's parent Control — scaling the Button itself can clip children.] --- #### Button (Secondary) **Category**: Input **Status**: Draft **When to Use**: Alternative or cancel action. "Back," "Cancel," "Skip," "Maybe Later." Lower visual weight than Primary — it should recede visually, not compete. **When NOT to Use**: Destructive actions (use Button (Destructive)). The most important action on the screen (use Button (Primary)). **Interaction Specification**: | State | Visual | Input | Response | Duration | Audio | |-------|--------|-------|----------|----------|-------| | Default | Outlined style (border only, transparent fill), secondary color. Slightly smaller or lower weight than Primary. | — | — | — | — | | Hovered | Background fill appears at 15% opacity. Border brightens. Scale 1.02x. | Mouse over | Transition from Default | 80ms ease-out | [UI hover sound — softer variant than Primary] | | Focused | Focus ring, same specification as Primary. | Tab / D-pad | Transition from Default | 80ms ease-out | [UI focus sound] | | Pressed | Scale 0.97x, fill opacity increases to 30% | Click / Enter / B (Xbox) / Circle (PS) on focused state | Action fires on press-up | 60ms ease-in | [UI cancel/back sound] | | Disabled | 40% opacity | — | No response | — | — | **Accessibility**: Same requirements as Button (Primary). Accessible name must match visible label. In a dialog with Primary and Secondary buttons, the Secondary button typically maps to the platform "cancel" input (B / Circle / Escape) as well as direct focus activation. **Implementation Notes**: [Same as Button (Primary). Where a Primary and Secondary appear together, ensure Secondary is always positioned consistently — right/bottom of Primary on horizontal layouts, or below Primary on vertical layouts. Consistency across screens is more important than per-screen aesthetic preference.] --- #### Button (Destructive) **Category**: Input **Status**: Draft **When to Use**: Any action that is irreversible and causes loss of player data or significant progress: "Delete Save File," "Reset All Settings," "Leave Match," "Discard Changes." The visual treatment signals danger before the player presses. **When NOT to Use**: Actions that can be undone, or actions that are merely consequential but reversible. **Interaction Specification**: | State | Visual | Input | Response | Duration | Audio | |-------|--------|-------|----------|----------|-------| | Default | Outlined or filled with destructive color (typically a desaturated red — confirm colorblind compatibility in accessibility-requirements). Label may include a warning icon. | — | — | — | — | | Hovered / Focused | Same behavior as Button (Primary) hover/focus but with destructive color | — | — | 80ms | [UI hover sound] | | Pressed (first press) | Does NOT execute the action. Instead, opens Confirmation Dialog pattern (see below). The button itself shows a brief pulse animation. | Click / Enter | Trigger Confirmation Dialog | 100ms pulse | [UI warning sound — distinct from standard confirm] | | — | Confirmation Dialog handles the actual execution | — | — | — | — | | Disabled | 40% opacity | — | No response | — | — | > **Critical rule**: A Button (Destructive) NEVER executes its action directly. > It always triggers a Confirmation Dialog. There are no exceptions. A player > who presses it by accident must always have one more opportunity to back out. > Games that skip confirmation on destructive actions generate the most visible > negative community sentiment of any UX failure type. See: every "accidentally > deleted save file" complaint on any game forum. **Accessibility**: Screen reader must announce the destructive nature: "[Label] button — this action cannot be undone." In addition to accessible name, use the `description` property if available to add the warning text. **Implementation Notes**: [Destructive button triggers a separate Confirmation Dialog scene. Pass the action callback to the dialog — the button itself does not hold the execution logic. This separation prevents accidental execution if the confirmation dialog has a bug.] --- #### Toggle **Category**: Input **Status**: Draft **When to Use**: Binary on/off settings where both states are equally valid and the current state must be visible at a glance. "Subtitles: On/Off," "Aim Assist: On/Off," "Notifications: On/Off." **When NOT to Use**: Selections from more than two options (use Dropdown). Actions that happen once rather than representing a persistent state (use Button). Cases where the consequence of toggling is complex enough to need explanation (show a description field alongside). **Interaction Specification**: | State | Visual | Input | Response | Duration | Audio | |-------|--------|-------|----------|----------|-------| | Off / Default | Track: muted fill. Thumb: leftmost position. Label: "Off" or state label. | — | — | — | — | | Hovered | Track brightens 10%. Cursor: pointer. | Mouse over | Transition | 60ms | [UI hover sound] | | Focused | Focus ring around entire toggle element (track + thumb). | Tab / D-pad | — | 60ms | [UI focus sound] | | Pressed / Activated | Thumb slides to right side. Track fill changes to active color. Label changes to "On" or active state label. State persists. | Click / Enter / A / Cross | Toggle state change. Fire onChange event. Persist value. | 150ms ease-in-out for slide | [Toggle ON sound] | | Pressed / Deactivated | Thumb slides to left. Track reverts to muted fill. | Same inputs | Toggle state change | 150ms ease-in-out | [Toggle OFF sound — subtly different from ON] | | Disabled | 40% opacity. No interaction. Current state still visible. | — | No response | — | — | **Accessibility**: - Keyboard/Gamepad: Space or Enter to toggle. Avoid requiring directional inputs (left/right) to toggle — some users cannot predict that behavior. - Screen reader: Role: "switch." State: "on" or "off" — the accessible name should NOT include the state (the screen reader announces state separately). Correct: accessible name "Subtitles," state "on." Incorrect: accessible name "Subtitles On." - The toggle label (not just the visual thumb position) must change to show current state for players who cannot reliably distinguish left from right positions. **Implementation Notes**: [Godot: Use a custom Control or a CheckButton. The built-in CheckButton provides accessibility role but uses a checkbox-style visual; a custom slide-toggle animation may be needed for the target art style. Ensure the slide animation is skipped when motion reduction mode is active — in that case, snap to final state instantly.] --- #### Slider **Category**: Input **Status**: Draft **When to Use**: Selecting a value from a continuous range where approximate values are acceptable and the range and relative position matter. Volume (0–100%), brightness, text size. The visual representation of position is itself useful information. **When NOT to Use**: Precise value entry (use Input Field). Selection from a short discrete list (use Dropdown). Binary state (use Toggle). **Interaction Specification**: | State | Visual | Input | Response | Duration | Audio | |-------|--------|-------|----------|----------|-------| | Default | Track (full width). Fill (left of thumb, shows current value). Thumb (draggable handle). Current value label (right of track or above thumb). | — | — | — | — | | Hovered | Thumb enlarges slightly (1.2x). Track brightens. | Mouse over | — | 60ms | — | | Focused | Focus ring on thumb. Track brightens. | Tab / D-pad | — | 60ms | [UI focus sound] | | Dragging (mouse) | Thumb follows cursor. Fill updates in real time. Value label updates in real time. | Click + drag on thumb | Continuous value update. Fire onChange continuously. | Real time | [Slider adjust sound — subtle, loops while dragging] | | Keyboard / D-pad adjust | Thumb moves one step (5% of range per press, or 1 discrete unit). | Left/Right arrows or Left/Right D-pad while focused | Step value change. Fire onChange per step. | Instant | [Slider step sound — one click per step] | | Keyboard fast adjust | Larger step (25% of range). | Page Up / Page Down while focused | Large step value change | Instant | [Same step sound] | | Released | Value locks. onChange fires final value. | Mouse release | — | — | — | | Disabled | 40% opacity. No interaction. Value visible. | — | No response | — | — | **Accessibility**: - Keyboard: Left/Right arrows to adjust by small step. Page Up/Page Down for large step. Home/End to jump to min/max. - Screen reader: Role: "slider." Accessible name: the label (e.g., "Music Volume"). Current value announced on every change: "Music Volume, 80 percent." Min/max values announced on first focus. - All sliders must show a numeric value alongside the visual position. Relying only on track fill position excludes players who cannot perceive relative position. **Implementation Notes**: [Godot `HSlider`: set `step` to appropriate increment. Override keyboard input to add Page Up/Down support via `_input()`. Bind the `value_changed` signal to update the displayed numeric label. When motion reduction mode is enabled, ensure value label updates are the sole feedback — do not suppress them. Rumble feedback on gamepad slider adjustment is a nice enhancement for accessibility.] --- #### Dropdown / Select **Category**: Input **Status**: Draft **When to Use**: Selection from a discrete list of 3-15 options where only the selected value needs to be visible at rest. Display resolution, language, window mode, input preset. The closed state shows only the current selection. **When NOT to Use**: Binary choices (use Toggle). More than ~15 options (use a full List pattern or a scrollable Select). When comparing options matters as much as selecting one (show options visibly, e.g., as a horizontal selector or list). **Interaction Specification**: | State | Visual | Input | Response | Duration | Audio | |-------|--------|-------|----------|----------|-------| | Closed / Default | Label (left). Current value (right). Chevron-down icon (far right). | — | — | — | — | | Hovered | Row background fills at 10% opacity | Mouse over | — | 60ms | — | | Focused (closed) | Focus ring on entire row. | Tab / D-pad | — | 60ms | [UI focus sound] | | Opening | Dropdown list appears below (or above if near screen bottom). List items visible. Previously selected item highlighted. Focus moves to selected item inside list. | Click / Enter / A / Cross | Open list | 100ms ease-out (expand) | [UI expand sound] | | List item hovered/focused | List item highlights | Mouse / D-pad | — | 60ms | [UI hover sound] | | List item selected | List closes. Closed state shows new value. onChange event fires. | Click / Enter / A / Cross on item | Select value, close list | 80ms ease-in (collapse) | [UI confirm sound] | | Dismissed without selecting | List closes. Value unchanged. | Escape / B / Circle / click outside | Dismiss | 80ms | [UI cancel sound] | | Disabled | 40% opacity. No interaction. | — | — | — | — | **Accessibility**: - Keyboard: Up/Down arrows navigate list items while open. Enter selects. Escape dismisses. First letter of an option jumps focus to first matching item. - Screen reader: Role: "combobox." Accessible name: the field label. Expanded/collapsed state announced. Current value announced when focused. Each list item announces its value and position: "English, 1 of 12." - The dropdown list must never obscure the current item or the control that opened it — this is a common failure on small screens. **Implementation Notes**: [Godot: Custom implementation using a `Button` (the closed state) and a `PopupMenu` or a `VBoxContainer` revealed by animation. Native `OptionButton` provides accessibility but limited visual customization. Ensure the popup positions itself above the control if it would be clipped by the screen bottom. Close the popup on `_input` detecting click outside its rect.] --- #### List Item **Category**: Layout / Input **Status**: Draft **When to Use**: A single selectable row in a vertically scrollable list. Achievements, quest log entries, settings categories, save file slots. The list is the container; this is the row within it. **When NOT to Use**: Grid layouts where items exist in two dimensions (use Grid Item). Non-selectable content rows (remove hover/focus states and the pressed state). **Interaction Specification**: | State | Visual | Input | Response | Duration | Audio | |-------|--------|-------|----------|----------|-------| | Default | Full-width row. Icon (optional, left). Primary label. Secondary label / metadata (right or below primary). Chevron (right, if navigates deeper). | — | — | — | — | | Hovered | Row background at 12% opacity highlight. | Mouse over | — | 60ms | — | | Focused | Focus ring on row OR row background at 20% opacity (consistent with platform convention). | D-pad / Tab | — | 60ms | [UI focus sound] | | Selected (persistent) | Row background at 25% opacity. May show a selection indicator (left border, checkmark). Distinct from focused state — a row can be selected but not focused. | — | Rendered state | — | — | | Pressed / Activated | Brief brightness flash, then navigates or performs action | Click / Enter / A / Cross | Navigation or action | 80ms flash | [UI confirm sound] | | Disabled | 40% opacity. No interaction. | — | — | — | — | **Accessibility**: - Keyboard/Gamepad: Up/Down arrows or D-pad to move between list items. The list must handle focus cycling — reaching the bottom should stop (not wrap) unless wrapping is explicitly designed. - Screen reader: Role: "listitem." Parent list role: "list." Accessible name: primary label content. Metadata (secondary label) is optionally included in the description. Position announced: "Quest Log, 3 of 12." - Minimum row height: 44pt / 48dp for touch. For controller-primary platforms, 56px rows are more comfortable. **Implementation Notes**: [Godot: Use a `VBoxContainer` inside a `ScrollContainer`. Each row is a custom `Control` or `PanelContainer` with a `_gui_input` override. For keyboard navigation inside the scroll container, implement custom focus traversal — Godot's default Tab navigation does not scroll the container to keep focused items in view. Use `ensure_control_visible()` on the scroll container.] --- #### Grid Item **Category**: Layout / Input **Status**: Draft **When to Use**: A selectable cell in a two-dimensional grid. Inventory slots, ability select, crafting ingredient selection, character portrait selection. The grid is the container; this is the cell. **When NOT to Use**: Single-column content (use List Item). Non-selectable display cells (remove interactive states). **Interaction Specification**: | State | Visual | Input | Response | Duration | Audio | |-------|--------|-------|----------|----------|-------| | Empty | Empty slot visual (subtle border or dashed outline). Different from disabled. | — | — | — | — | | Populated | Item icon fills cell. Stack count (bottom right, if applicable). Quality indicator (border color or icon overlay). | — | — | — | — | | Hovered | Brightness +15%. Tooltip appears after 400ms delay. | Mouse over | — | 60ms | — | | Focused | Focus ring (2px, offset 2px). Same brightness as hovered. Tooltip appears after 400ms delay or immediately on gamepad. | D-pad navigation | — | 60ms | [UI focus sound] | | Selected (persistent) | Distinct border (thicker, contrasting color). May show selection checkmark. | Click / Enter / A / Cross | Select item. Can coexist with focused state on a different cell. | Instant | [UI select sound] | | Pressed | Brief scale 0.95x, then executes action | Double-click / Enter / A / Cross | Action (equip, use, inspect — defined by context) | 80ms | [UI confirm sound] | | Locked | Padlock overlay icon on populated content. No hover/focus states. | — | No interaction | — | — | | Drag source | Cell dims (50% opacity), drag preview appears at cursor. | Click + drag (mouse only) | Begin drag operation | Instant | [UI grab sound] | | Drop target (valid) | Cell brightens, accepting color indicator | Item dragged over | — | 60ms | — | | Drop target (invalid) | Red tint or shake animation | Item dragged over invalid slot | — | 60ms | [UI error sound] | **Accessibility**: - Keyboard/Gamepad: D-pad or arrow keys navigate cells. The grid must communicate its dimensions to screen readers. Row/column position announced. - Screen reader: Role: "gridcell." Parent role: "grid." Accessible name: item name (or "empty slot" for empty cells). State: "selected" when selected, "dimmed" when locked. Position: "row 2, column 3." - Tooltips must be reachable by keyboard — they must appear when the cell is focused, not only when hovered. **Implementation Notes**: [Godot: `GridContainer` with fixed column count. Each cell is a custom `Control`. Implement custom D-pad navigation by overriding `_gui_input` and calculating the cell to the left/right/above/below based on index and column count. `GridContainer` does not provide this natively.] --- #### Modal Dialog **Category**: Feedback / Layout **Status**: Draft **When to Use**: A decision or acknowledgment that must be resolved before the player can continue. The dialog is blocking — background content is dimmed and non-interactive. "Are you sure?", "Your progress will be saved.", error states. **When NOT to Use**: Non-blocking notifications (use Toast / Notification). Information that can wait until the player is ready (add it to a persistent help system instead). Dialogs that should allow the player to continue playing behind them. **Interaction Specification**: | State | Visual | Input | Response | Duration | Audio | |-------|--------|-------|----------|----------|-------| | Opening | Background overlay animates from 0 to 60% opacity. Dialog panel scales from 0.9 to 1.0. Dialog enters from center (not from an edge). | Triggered by code | Focus moves to first interactive element in dialog (or the Primary button) | 200ms ease-out | [UI modal open sound] | | Active | Background non-interactive. Dialog has all input focus. Player cannot interact with background. | Keyboard / gamepad navigates within dialog only | — | — | — | | Dismissing (confirmed) | Dialog panel scales to 1.1 then fades. Overlay fades to 0%. | Primary button pressed | Execute action, return focus to trigger element | 180ms | [UI confirm sound] | | Dismissing (cancelled) | Dialog panel scales to 0.9 then fades. Overlay fades to 0%. | Secondary button / Escape / B / Circle | No action, return focus to trigger element | 150ms | [UI cancel sound] | | Cannot dismiss | If the dialog represents a blocking error, do not provide a cancel path. Provide only resolution options. | — | — | — | — | > **Focus trap rule**: While a modal dialog is open, Tab and D-pad navigation > must cycle within the dialog's interactive elements only. It must not be possible > to navigate focus outside the dialog to the background content. This is both > an accessibility requirement (WCAG 2.1 SC 2.1.2) and a UX integrity requirement. > When the dialog closes, focus must return to the element that triggered it, > not to the top of the page. **Accessibility**: - Screen reader: Dialog container role: "dialog." Accessible name: dialog title (required — every dialog must have a title, even if visually hidden). On open, screen reader announces dialog title and first focusable element. Focus trap active. - Keyboard: Escape key always maps to the cancel/dismiss action (same as Secondary button or close button). Enter always maps to the primary/confirm action. - Motion reduction: Scale animation replaced with instant appear/disappear. Overlay fade retained at 100ms (faster). **Implementation Notes**: [Godot: Implement as a `CanvasLayer` with a high layer value (100+) to ensure it renders above all game content. The background overlay is a full-screen `ColorRect` at 60% black opacity. Use `grab_focus()` on the dialog's primary button after the open animation completes. Override `_input()` to implement the focus trap — intercept Tab navigation and reroute to the dialog's focusable elements.] --- #### Confirmation Dialog **Category**: Feedback / Layout **Status**: Draft **When to Use**: The specific case of confirming a destructive action. Always triggered by Button (Destructive). Always has exactly two options: confirm (labeled with the specific action, not "OK") and cancel. **When NOT to Use**: Non-destructive confirmations. Errors or notifications that do not require a decision. Any dialog with more than two actions. > **Label rule**: The confirm button must be labeled with the specific action, > not a generic "OK" or "Yes." "Delete Save File" not "OK." "Leave Match" not > "Yes." This reduces mistakes for players who have difficulty reading the dialog > content quickly. The pattern comes from Apple HIG and is validated by decades > of usability research. **Structure**: - Title: Brief, action-describing. "Delete save file?" not "Are you sure?" - Body: One sentence stating the consequence. "This cannot be undone." - Confirm button: Button (Primary) — labeled with the specific action. "Delete Save File." - Cancel button: Button (Secondary) — "Cancel." - Default focus: Cancel (safer default — reduces accidental destructive actions). **Accessibility**: Inherits all Modal Dialog accessibility. Additionally: screen reader announces "Alert dialog, [title]" to signal destructive context. Default focus on Cancel is a requirement, not a preference. **Implementation Notes**: [Confirmation Dialog is a specific instance of Modal Dialog — implement it as a subclass or as a parameterized scene. The default focus on Cancel is critical: set `grab_focus()` on the Cancel button, not the Confirm button, after open animation completes.] --- #### Toast / Notification **Category**: Feedback **Status**: Draft **When to Use**: Brief, non-blocking information that does not require a player decision. "Game saved." "Achievement unlocked." "Your inventory is full." The player can continue playing; the notification disappears on its own. **When NOT to Use**: Information that requires a decision (use Modal Dialog). Errors that require the player to take action. Critical information that the player must not miss. **Interaction Specification**: | State | Visual | Input | Response | Duration | Audio | |-------|--------|-------|----------|----------|-------| | Entering | Slides in from screen edge (typically bottom-right, away from primary action areas). Fades from 0 to 100% opacity. | Triggered by code | — | 200ms ease-out | [Sound matching notification type — see Sound Standards] | | Displayed | Full opacity. Optional: icon (left), title, body text (optional), dismiss button (X, optional). | Pointer hover pauses auto-dismiss timer | Pause auto-dismiss | — | — | | Auto-dismiss | Fades from 100 to 0% opacity, slides out | Timer expires (5 seconds default for one-line; 8 seconds for two-line) | Remove from queue | 200ms ease-in | — | | Manual dismiss | Fades and slides out immediately | Click/tap X button or swipe on touch | Remove | 150ms | [UI cancel sound, quiet] | | Queue overflow | New notification pushes oldest out early | New notification triggered while previous is displayed | FIFO queue, max 3 simultaneous | — | — | **Accessibility**: - Screen reader: Toasts must be read aloud without requiring focus. In HTML, this uses `role="status"` or `role="alert"`. In game UI, this requires the engine's accessibility notification system. Verify engine support in engine-reference docs. - Motion reduction: Slide animation replaced with fade only. - Toasts must never be the sole communication channel for information the player needs to act on. If the information requires action, use a persistent UI element in addition to the toast. - Auto-dismiss timer: 5 seconds is the minimum. Players with cognitive processing differences may need more time. Consider a setting to extend to 10 or 15 seconds. **Implementation Notes**: [Godot: Manage a queue of `PanelContainer` scenes in a `VBoxContainer` anchored to a screen corner. Each toast is instantiated, added to the container, then auto-removed after a timer. The container should be on a high `CanvasLayer` (50+) but below modal dialogs (100+). Animate using a `Tween` on `modulate.a` and `position.x`. When motion reduction is active, skip the position animation.] --- #### Tooltip **Category**: Feedback **Status**: Draft **When to Use**: Contextual information that supplements a visible label. Item descriptions in inventory. Stat explanations on a character sheet. Setting descriptions in accessibility options. The player must be able to access this information or proceed without it. **When NOT to Use**: Information the player MUST read to complete an action — put that in the label or body text, not a tooltip. Tooltips are not discoverable on mobile touch without a hover state. On touch-only platforms, use an info button that opens a description modal instead. **Interaction Specification**: | State | Visual | Input | Response | Duration | Audio | |-------|--------|-------|----------|----------|-------| | Hidden | — | — | — | — | — | | Hover trigger | — | Mouse enters element | Begin 400ms delay timer | — | — | | Gamepad/keyboard trigger | — | Element receives focus | Begin 300ms delay timer (shorter because navigation is intentional) | — | — | | Appearing | Tooltip panel fades in and scales from 0.95 to 1.0. Positioned near element (prefer above, adjust if near screen edge). | Timer expires | Show tooltip | 120ms ease-out | — | | Displayed | Tooltip visible. Title (optional). Body text. Max width: 300px. Multiple lines allowed. | — | — | — | — | | Hiding | Tooltip fades out | Mouse leaves element / focus moves away | Hide tooltip | 80ms ease-in | — | **Accessibility**: - Screen reader: Tooltip content must be accessible without hover. The accessible name of the parent element should include the most critical tooltip information. The full tooltip text is optionally in the `description` property. Screen reader reads tooltip content when element is focused. - The delay (300-400ms) prevents accidental tooltip display and is required — instant tooltips are disruptive in gamepad navigation. - Tooltip text must meet the same contrast requirements as body text (4.5:1 minimum). **Implementation Notes**: [Godot: Attach a custom `TooltipControl` scene as a child of the trigger element. Show/hide with a `Timer` node. Position the tooltip using a `CanvasLayer` to ensure it appears above all other UI. For screen edges, detect if the tooltip rect extends beyond `get_viewport_rect()` and flip the position to the opposite side.] --- #### Progress Bar **Category**: Feedback / Layout **Status**: Draft **When to Use**: Linear progress toward a defined endpoint. Loading screens (time to completion), XP fill toward next level, quest objectives with countable progress ("3 of 10 enemies defeated"), download progress. **When NOT to Use**: Circular or radial progress (use a separate Radial Progress pattern if needed). Values that fluctuate up and down rapidly (use Health/Resource Bar pattern). Values with no defined endpoint. **Interaction Specification**: | State | Visual | Input | Response | Duration | Audio | |-------|--------|-------|----------|----------|-------| | Default | Track (full width, background color). Fill (left to right, value color). Value label (percentage or N/M, outside or inside fill). | — | — | — | — | | Value increasing | Fill width animates to new value | Value changes | Smooth fill animation | 300ms ease-out | [Context-dependent — XP gain has a sound; loading has none] | | Value at maximum | Fill reaches full width. Optional: completion animation (pulse, glow). | Value reaches 100% | Completion event fires | 200ms | [Completion sound if appropriate] | | Value at zero | Fill hidden (zero width). Track still visible. | — | — | — | — | | Indeterminate (unknown duration) | Animated loop (fill segment moves left-to-right, repeat). Used for loading of unknown duration. | — | — | Infinite loop | — | **Accessibility**: - Screen reader: Role: "progressbar." Accessible name: what is progressing (e.g., "Experience Points," "Loading"). Value: current numeric value AND percentage AND maximum. "Experience Points, 450 of 1000, 45 percent." Update on significant changes (not every pixel). - Do not rely only on fill color to communicate value. Include a numeric label. - Indeterminate progress bars: announce "Loading, in progress" — do not announce changes since the value is unknown. - Motion reduction: Indeterminate animation is replaced with a static "loading" indicator. Smooth fill animation is replaced with instant jump to new value. **Implementation Notes**: [Godot: `ProgressBar` built-in with custom theming. For indeterminate mode, `ProgressBar` does not have a native indeterminate state in Godot 4.x — implement using a looping `Tween` on a fill element's position. Ensure the Tween is paused when motion reduction mode is active and a static indicator is shown instead.] --- #### Input Field **Category**: Input **Status**: Draft **When to Use**: Text entry. Player name on a new save, search within a list, remapping a key binding (special case — shows the key press, not typed text), entering a numeric value precisely. **When NOT to Use**: Selecting from known options (use Dropdown or List). On console-primary platforms, minimize text entry — it requires a virtual keyboard, which is high friction. **Interaction Specification**: | State | Visual | Input | Response | Duration | Audio | |-------|--------|-------|----------|----------|-------| | Default | Field border, placeholder text (label-style, muted color), empty input area. | — | — | — | — | | Hovered | Border brightens slightly | Mouse over | — | 60ms | — | | Focused | Border brightens fully. Cursor (blinking, 530ms on/530ms off). Placeholder text hidden. | Tab / click | Open virtual keyboard on console/mobile | Instant | [UI focus sound] | | Typing | Characters appear. Cursor advances. | Keyboard input | Update field value | Immediate | [Subtle keystroke sound, optional] | | Value present | Field shows typed value. Placeholder hidden. Clear button appears (X, right of field) if value is non-empty. | — | — | — | — | | Character limit reached | No further input accepted. Optional: brief shake animation and limit indicator changes color. | Input at limit | Reject further characters | 200ms shake | [UI error sound, subtle] | | Clear | Field empties. Cursor returns. Clear button disappears. | Click X / gamepad clear input | Clear value | Instant | [UI cancel sound, subtle] | | Validation error | Border turns error color (red — ensure colorblind safe). Error message appears below field. | On submit or on blur | Show error | Instant | [UI error sound] | | Validated / correct | Border turns success color (green — ensure colorblind safe). Success icon optional. | On validation pass | — | Instant | — | | Disabled | 40% opacity, no interaction. Value still visible. | — | — | — | — | **Accessibility**: - Keyboard: All standard text editing shortcuts (Home, End, Ctrl+A, Ctrl+C, Ctrl+V, Ctrl+Z). - Screen reader: Role: "textbox." Accessible name: field label (not placeholder text). Current value announced. Character limit announced when reached. Validation errors announced immediately on occurrence. - Placeholder text must not be used as the only label — a visible label above or beside the field is required. Placeholder text disappears when the player types, causing confusion for players with cognitive or memory impairments. **Implementation Notes**: [Godot `LineEdit`: set `placeholder_text` for the hint but always include a visible `Label` node as the field's accessible name. Bind `text_changed` signal for real-time validation. Bind `text_submitted` for form submission on Enter. On console, `LineEdit.call("_popup_keyboard")` or use the OS virtual keyboard API — verify against engine-reference/godot/ for Godot 4.6 console keyboard API specifics.] --- #### Tab Bar **Category**: Navigation **Status**: Draft **When to Use**: Dividing a single screen's content into discrete sections where only one section is visible at a time. Character sheet tabs (Stats / Equipment / Skills), settings tabs (Gameplay / Graphics / Audio / Accessibility). Maximum 5-6 tabs before the pattern breaks down and a sidebar navigation should be considered instead. **When NOT to Use**: More than 6 tabs. Content that benefits from simultaneous visibility (use a layout pattern instead). Navigation between different screens (use Screen Push). **Interaction Specification**: | State | Visual | Input | Response | Duration | Audio | |-------|--------|-------|----------|----------|-------| | Default (inactive tab) | Tab label. No active indicator. | — | — | — | — | | Active tab | Tab label. Active indicator (underline, fill, or contrasting background). Content area shows this tab's content. | — | — | — | — | | Hovered (inactive) | Tab background fills slightly | Mouse over | — | 60ms | — | | Focused (keyboard/gamepad) | Focus ring on tab label. | Tab key (within tab bar) or D-pad left/right on tab row | — | 60ms | [UI focus sound] | | Activated | Active indicator transitions to this tab. Content area transitions (fade or slide). | Click / Enter / A / Cross | Switch active tab. Content update. | 150ms ease | [UI tab switch sound] | | Gamepad shoulder button | — | L1/R1 (PS) or LB/RB (Xbox) | Switch to previous/next tab (standard platform convention) | 150ms | [UI tab switch sound] | **Accessibility**: - Keyboard: Arrow keys navigate between tabs within the tab bar (left/right). Tab key moves focus into the content area below. This follows the ARIA tab panel pattern. - Screen reader: Role: "tab" for individual tabs. Role: "tablist" for the container. Role: "tabpanel" for the content area. Active tab state: "selected." Accessible name: tab label. Tabpanel is labeled by its corresponding tab. - The active tab must be visually distinguishable by more than color alone (underline, fill pattern, or weight change in addition to color). **Implementation Notes**: [Godot: `TabContainer` built-in. For custom visual styling, implement manually with a `HBoxContainer` of tab buttons and a `MarginContainer` for content. The shoulder button shortcut (LB/RB) must be implemented in the screen's `_input()` override — it is not built into Godot's tab system. Check platform conventions: Xbox uses LB/RB; PlayStation uses L1/R1; both are the same physical button, so a single binding works.] --- #### Scroll Container **Category**: Layout **Status**: Draft **When to Use**: Content that exceeds the visible area of its container. Inventory lists, lore entry text, credits, long settings lists. The scroll indicator shows the player that more content exists. **When NOT to Use**: Content that can be paginated instead (pagination may be clearer for dense list navigation). Infinite scroll (always provide a loading state and an end state). **Interaction Specification**: | State | Visual | Input | Response | Duration | Audio | |-------|--------|-------|----------|----------|-------| | Content fits | No scrollbar visible (or always-visible scrollbar at full height, depending on art direction). | — | — | — | — | | Scrollable | Scrollbar appears (right edge). Scrollbar thumb size represents viewport vs. content ratio. | — | — | — | — | | Scrolling (mouse) | Content moves. Scrollbar thumb moves proportionally. | Mouse wheel | Scroll by 3 lines per wheel tick (configurable in OS) | Smooth | — | | Scrollbar drag | Content moves. Thumb follows pointer. | Click + drag scrollbar thumb | Scroll proportionally | Real time | — | | Keyboard scroll | Content moves one item height per keypress. | Up/Down arrows when container is focused and no child is focused | Scroll by one unit | Immediate | — | | Gamepad scroll | Content moves to keep focused item in view. | D-pad navigation to items beyond visible area | Auto-scroll to keep focused item visible | Smooth 150ms | — | | Scroll top / bottom | Content stops. Scrollbar thumb at end. | Content boundary reached | Stop scrolling | — | — | | Focus follows scroll | When a child element receives focus, scroll container ensures it is fully visible. | Any child receives focus | Scroll to reveal focused element | 200ms ease | — | **Accessibility**: - Keyboard/Gamepad: The scroll container itself should not require explicit scrollbar interaction — navigating list items inside it should auto-scroll to keep focused items in view. - Screen reader: The scroll container announces "scrollable" and the scroll position ("showing items 5 through 15 of 30"). This requires engine accessibility support — verify in engine-reference/godot/. - Fade edges (content fading at scroll boundaries to indicate more content exists) are a helpful visual affordance but must not be the only indicator that content exists beyond the visible area. Include a scrollbar. **Implementation Notes**: [Godot `ScrollContainer`: call `ensure_control_visible()` on the focused child whenever `gui_focus_changed` fires inside the container. Bind this via a recursive `connect` on the container's `gui_focus_changed` signal. For smooth scroll animation, use a `Tween` on `scroll_vertical` rather than setting it directly.] ---