live_interaction_contracts

Executable interaction contracts for Phoenix LiveView.

This package checks whether browser-native interaction state survives LiveView patches. It ships a conformance harness, a delegation ledger, and a small set of unstyled reference components that are backed by those contracts.

Use it when you need to know whether a popover, menu, tooltip, select, combobox, dialog adapter, focus handoff, or async query shell is safe under LiveView DOM patching.

Why this exists

LiveView gives the server authority over rendered HTML, but many interaction machines keep their state in the browser: top-layer popovers, active descendants, focus, selection, pending input, and listbox shells.

The hard failures happen when both sides think they own the same state. A patch replaces a node, a client hook rebinds, the server renders stale results, or the browser keeps state that the server cannot observe.

live_interaction_contracts turns those cases into executable contracts. Each green claim is backed by browser tests across Chromium, Firefox, and WebKit. Amber and unknown cases stay explicit.

What ships

The components are intentionally unstyled. They are reference primitives, not a finished UI kit. The future UI kit direction is Temper UI; this package is the proof layer underneath it.

Install

For harness, ledger, and CI use:

{:live_interaction_contracts, "~> 1.0", only: [:dev, :test], runtime: false}

If you also use the reference components at runtime:

{:live_interaction_contracts, "~> 1.0"}

The harness requires elixir, node, and Playwright browsers:

npx playwright install chromium firefox webkit

The reference components require Phoenix LiveView 1.1 or later. Their client JS ships as Phoenix colocated hooks. After mix compile, register the extracted hooks in your app.js:

import {hooks as licHooks} from "phoenix-colocated/live_interaction_contracts";
new LiveSocket("/live", Socket, {hooks: {...hooks, ...licHooks}});

Reference component example

defmodule MyAppWeb.Demo do
use Phoenix.Component
use LiveInteractionContracts.Components
def demo(assigns) do
~H"""
<.popover id="demo" placement="bottom">
<:trigger>Open</:trigger>
<:content>Patch-safe by contract.</:content>
</.popover>
"""
end
end

Run the harness

Run these commands from your Phoenix project:

# all stable browser engines
mix live_interaction_contracts.test
# one browser
mix live_interaction_contracts.test --browser chromium
# one suite
mix live_interaction_contracts.test --suite combobox
# early warning against LiveView main
mix live_interaction_contracts.test --lv "github:phoenixframework/phoenix_live_view#main"
# regenerate the delegation ledger
mix live_interaction_contracts.ledger
# advisory app audit for patch-safety and dual-write risk
mix live_interaction_contracts.audit --path /path/to/app
# compare two harness result sets
mix live_interaction_contracts.compare --a before.json --b after.json

By default, the harness tests against your project's LiveView version.

Current v1 coverage

Green reference machines:

Amber:

Unknown: