Skip to content

@pixelscriptio/tessera/behavior ​

Headless interaction primitives, such as press. See Behaviors.

attachCombobox ​

function

Combobox: the keyboard of an editable combobox with a listbox popup (the ARIA 1.2 pattern), on the text input.

It is the keys and the attributes, nothing else. The component owns the text, the options, which one is active and whether the list is open, and binds aria-expanded and aria-activedescendant on the input. The behavior reads those from the element, turns keys into commands, and the component moves its state:

  • Arrow Down opens the list (or moves to the next option); Arrow Up opens it on the last option (or moves back).
  • Enter chooses the active option, and does not submit a form while it does. With no active option it does nothing.
  • Escape closes an open list and keeps what was typed.
  • Tab closes an open list and leaves the field as usual.
  • It sets role="combobox", aria-autocomplete="list", aria-expanded="false" and autocomplete="off" if the input has none of them, so the pattern is complete even if the component forgot one.
  • A disabled input (or one with aria-disabled="true") gives no commands. Keys during text composition (an input method editor) are left alone.

Returns a function that removes everything it added.

ts
function attachCombobox<N, E extends N>(adapter: RendererAdapter<N, E>, input: E, options: ComboboxOptions): () => void

attachDialog ​

function

Dialog: a modal dialog on a native <dialog> element.

The browser does the hard parts of a modal: showModal() makes everything behind it inert, keeps focus inside, moves focus in, and gives it back to what opened it on close. The behavior adds what it does not:

  • It follows data-open. The component owns whether it is open and binds data-open="true" to its state; the behavior shows the dialog modally when that is set and closes it when it is removed.
  • Dismissing is a request. Escape (the browser's cancel), a click outside the panel and the browser closing the dialog by itself are reported as dismiss with a reason, and the dialog stays as it is until the component moves its state. A component that wants to confirm first can refuse.
  • Tab stays inside. Tab on the last control goes to the first, and Shift+Tab on the first goes to the last, so focus cannot leave through the browser's own controls.
  • The page does not scroll behind it, and the scrollbar's width is kept so nothing shifts. Nested dialogs share one lock.

Returns a function that removes everything it added and closes the dialog.

ts
function attachDialog<N, E extends N>(adapter: RendererAdapter<N, E>, el: E, options: DialogOptions): () => void

attachDisclosure ​

function

Disclosure: a trigger that shows and hides a region, for an accordion item or a "show more".

Attach it to the trigger. It is press, reading the current state from aria-expanded on the trigger (the component binds it, and owns the state) and reporting where it is going. It starts as collapsed if the trigger has no state yet. Showing and hiding the content is the component's job: bind hidden to the same state.

Returns a function that removes everything it added.

ts
function attachDisclosure<N, E extends N>(adapter: RendererAdapter<N, E>, trigger: E, options: DisclosureOptions): () => void

attachPress ​

function

Press: a click, a tap, or Enter or Space on the element, with none of the differences between them.

  • Disabled is read from the element (disabled or aria-disabled="true"), so it follows whatever the template binds. A disabled element never presses.
  • While the pointer or Space or Enter is held, the element has data-state="pressed", so styles can use it without JavaScript.
  • On an element that is not natively pressable it adds tabindex="0" and the role, listens for Enter (on keydown) and Space (on keyup, after preventing the page from scrolling), and ignores key repeat.

Works with no styles applied, and talks to the element only through the renderer adapter. Returns a function that removes everything it added.

ts
function attachPress<N, E extends N>(adapter: RendererAdapter<N, E>, el: E, options: PressOptions): () => void

attachTabs ​

function

Tabs: the keys and the pointer of a tab list (the ARIA tabs pattern, with automatic activation), on the element that has role="tablist". It needs a DOM: it finds the tabs and moves focus.

It does not own which tab is selected. The component does, and binds on each tab aria-selected, tabindex (0 for the selected one, -1 for the others, so Tab leaves the list) and aria-controls, and on each panel hidden and aria-labelledby. The behavior reports where the person is going and the component moves its state:

  • Arrow Right and Arrow Left (Down and Up when the list has aria-orientation="vertical") focus the next and previous tab and activate it, wrapping at the ends. Home and End go to the first and last.
  • A click on a tab activates it (so does Enter or Space on a <button>, which the browser turns into a click).
  • A disabled tab (disabled, or aria-disabled="true") is skipped by the keys and gives no click.
  • It sets role="tablist" if the element has none.

Returns a function that removes everything it added.

ts
function attachTabs<N, E extends N>(adapter: RendererAdapter<N, E>, list: E, options: TabsOptions): () => void

attachToggle ​

function

Toggle: press that moves between on and off, for a checkbox, a switch or a toggle button.

  • The current state is read from the element (aria-checked, or aria-pressed for a toggle button), so the component stays the owner of the value: it binds the attribute, and reacts to the event by setting its own state. The event says where it is going, never that it got there.
  • Space toggles. Enter toggles a switch and a toggle button but not a checkbox, which is what browsers do for a native one. Everything else about activation (disabled, key repeat, data-state="pressed") is press.
  • Starts as off if the element has no state yet, so assistive technology always has a value to read.

Returns a function that removes everything it added.

ts
function attachToggle<N, E extends N>(adapter: RendererAdapter<N, E>, el: E, options: ToggleOptions): () => void

BEHAVIOR_ATTACH ​

constant

The function each behavior is attached with, exported from @pixelscriptio/tessera/behavior.

ts
const BEHAVIOR_ATTACH: Readonly<Record<string, string>>

BEHAVIOR_EVENTS ​

constant

Behavior event name to the behavior that produces it, so @press in a template can be wired.

ts
const BEHAVIOR_EVENTS: Readonly<Record<string, string>>

BEHAVIOR_REQUIREMENTS ​

constant

What a contract must declare to use each behavior. The compiler validates "behavior" against this table.

ts
const BEHAVIOR_REQUIREMENTS: Readonly<Record<string, BehaviorRequirements>>

BehaviorRequirements ​

interface

Not documented yet.

ts
export interface BehaviorRequirements {
  
  parts: readonly string[]
  
  events: readonly string[]
  
  states: readonly string[]
  
  implemented: boolean
}

ComboboxCommand ​

interface

What a command event carries: what the person asked the combobox to do, from the keyboard.

ts
export interface ComboboxCommand {
  
  command: 'open' | 'open-last' | 'next' | 'prev' | 'select' | 'close'
  source: 'keyboard'
  event: unknown
}

ComboboxOptions ​

interface

Not documented yet.

ts
export interface ComboboxOptions {
  onCommand: (detail: ComboboxCommand) => void
  native?: boolean
  role?: string
}

DialogOptions ​

interface

Not documented yet.

ts
export interface DialogOptions {
  onDismiss: (detail: DismissDetail) => void
  
  native?: boolean
  role?: string
}

DisclosureOptions ​

interface

Not documented yet.

ts
export interface DisclosureOptions {
  onExpand: (detail: ExpandDetail) => void
  
  native?: boolean
}

DismissDetail ​

interface

What a dismiss event carries.

ts
export interface DismissDetail {
  
  reason: 'escape' | 'backdrop' | 'native'
  
  event: unknown
}

ExpandDetail ​

interface

What an expand event carries.

ts
export interface ExpandDetail {
  
  expanded: boolean
  source: PressDetail['source']
  event: unknown
}

PressDetail ​

interface

What a press event carries.

ts
export interface PressDetail {
  
  source: 'pointer' | 'keyboard' | 'virtual'
  
  event: unknown
}

PressOptions ​

interface

Not documented yet.

ts
export interface PressOptions {
  onPress: (detail: PressDetail) => void
  
  native?: boolean
  
  role?: string
}

TabsActivate ​

interface

What an activate event carries: the tab the person chose, from the keyboard or the pointer.

ts
export interface TabsActivate {
  
  value: string
  source: 'keyboard' | 'pointer'
  event: unknown
}

TabsOptions ​

interface

Not documented yet.

ts
export interface TabsOptions {
  onActivate: (detail: TabsActivate) => void
}

toggleAttribute ​

constant

A toggle button is aria-pressed; a checkbox and a switch are aria-checked.

ts
const toggleAttribute: (role: string | undefined) => "aria-pressed" | "aria-checked"

ToggleDetail ​

interface

What a toggle event carries.

ts
export interface ToggleDetail {
  
  checked: boolean
  
  source: PressDetail['source']
  
  event: unknown
}

ToggleOptions ​

interface

Not documented yet.

ts
export interface ToggleOptions {
  onToggle: (detail: ToggleDetail) => void
  
  native?: boolean
  
  role?: string
}