Browser
Browser UI kit
The kit under priv/ui is CSS and small JavaScript modules. It does not own the socket, the store, the router, or the message schema. Public class names start with wg-. Utility classes start with wg-u-, so they can sit beside another utility set. Message text is written with textContent. There is no helper that assigns a message body to innerHTML.
Install paths
mix wiregrid.ui.install
mix wiregrid.ui.install priv/static/vendor/wiregrid --force
The default destination is priv/static/vendor/wiregrid. Existing files are left in place unless --force is passed. The task copies the prebuilt bundle, the ESM bridge, and the CSS layers from the package’s priv/ui.
scripts/install.sh copies the same tree to share/wiregrid/ui under the prefix. A static page can load wiregrid.css and wiregrid.js from there. A bundler imports wiregrid.esm.js.
@import "./wiregrid/wiregrid.css";
:root {
--wg-accent: #7c8cff;
--wg-sidebar: 280px;
--wg-message-gap: 6px;
}
Layers
tokens.cssbase.csslayout.csscomponents.csschat.cssutilities.cssmotion.css
wiregrid.css imports the full stack. Import the layers yourself when the application already has a reset. Themes use data-wg-theme="dark" and data-wg-theme="light" on a wg-root element. Density uses data-wg-density="compact", comfortable, or cozy. Preset files such as midnight, soft, and plainwire are examples. A product theme should override tokens (--wg-bg, --wg-panel, --wg-text, --wg-accent, --wg-sidebar, --wg-message-gap, radii) and add component CSS only where the shape actually changes.
Classes you will use
Layout: wg-root, wg-app, wg-sidebar, wg-members, wg-main, wg-header, wg-pane, wg-stack, wg-inline, wg-split, wg-scroll. Chrome: wg-button with wg-button--primary, --danger, --ghost, --icon, --sm, --lg; wg-input; wg-nav-item; wg-title. Chat: wg-message, wg-reaction, channel and member lists, replies, attachments, composers, typing rows, thread panels, unread separators. App pieces include menus, tabs, dialogs, banners, switches, toasts, and skeletons. wg-sr-only hides text visually and leaves it available to assistive tech. Interactive helpers use normal focusable elements. The motion layer follows prefers-reduced-motion.
JavaScript
import {
appendMessage,
bindChatActions,
bindComposer,
createOverlay,
createStore,
setTheme,
connectChat
} from "./wiregrid/wiregrid.esm.js";
wiregrid.esm.js re-exports js/core.js, js/chat.js, js/composer.js, js/overlays.js, js/liveview.js, and js/client.js. The global wiregrid object on the classic bundle exposes version ("1.0.0"), createStore, setTheme, restoreTheme, setDensity, appendMessage, upsertMessage, bindChatActions, renderMessage, jumpToMessage, bindComposer (also as mountComposer), createOverlay, bindTabs, WiregridHooks, and connectChat.
appendMessage and upsertMessage keep a bounded set of nodes. trimTimeline defaults to 2,000 retained messages. bindComposer handles enter-to-send, textarea growth, and a UTF-8 byte limit. createOverlay traps focus and restores it on close. text() in core.js creates an element and assigns textContent.
connectChat(url, handlers) speaks Wiregrid.Transport.Protocol.JSON over a WebSocket, using the same topic strings as the C gateway. Point Cowboy at that protocol and send text frames. chat.html is a static shell. Open it beside wiregrid.css and add ?socket=ws://127.0.0.1:PORT/chat when a JSON socket is listening. The page subscribes to general, appends with textContent, and acks deliveries.
js/liveview.js exports WiregridHooks for the composer and delegated message actions. The hooks call pushEvent. They do not open a second socket.
Message rows use content-visibility where the browser supports it. For a timeline that must keep tens of thousands of rows mounted, pair the CSS with a windowing library. Custom markup you build from classes alone still needs the application to supply labels, focus order, and live regions.