Themes and widgets need
ravi.bot@next and a Console login (ravi login). @latest has no ravi themes or ravi widgets command. Install @next with bun add -g ravi.bot@next, or switch an existing install with ravi update --next.Concepts
Themes and widgets are Library items, like Library skills: they belong to the organization or to one project, have versions, and go through proposals and review.
Publish a theme
1
Start from the default theme
theme.json and fields.json with Ravi’s default theme. The folder name becomes the theme’s slug unless you pass --slug when you publish.2
Edit the tokens
theme.json uses the W3C Design Tokens format. Change what you need; anything you leave out keeps the default.fields.json (how each field type shows by default, such as status as a pill or as steps), logo.svg (at most 64 KiB) and a README.md.3
Check it
file:line:column severity code message, and any error exits 2. A theme whose text and background colors fall below the minimum contrast cannot be published (see Accessibility).4
Publish it
--scope org makes it available to every project; --scope <project> to one project. Without --scope, it goes to your default Console project.5
Make it the active theme
--project, use sets the organization’s theme. With --project, it sets that project’s, which wins over the organization’s. none removes it.LIBRARY_AGENT_CANNOT_PUBLISH, LIBRARY_AGENT_CANNOT_SET_THEME, exit 3). Reviewers comment on and publish proposals in the Console Library.
When pages change
ravi pages ship puts the active theme into every HTML file of the page.
- A
privateroute shipped without--themefollows the active theme: the next time someone opens it, it picks up the current version. - Every other route (
public,protected_link, password-protected, or shipped with--theme) keeps the theme it was shipped with until you ship it again.shipwarns when a route’s theme changed since its last publish. ravi pages ship --theme org/otheruses another theme for that ship, and--theme noneships without a Library theme.ravi pages outdatedlists the routes shipped with an older theme, widget or page runtime. Ship them again to update them.
color: var(--color-text-primary, #17202a) and background: var(--color-background-primary, #fbfcfd). The variables use the MCP Apps names (--color-*, --font-*, --border-radius-*, --shadow-*) plus Ravi’s own (--ravi-color-accent, --ravi-option-<color>-bg, --ravi-space-1 to --ravi-space-6). Dark mode follows the reader’s system setting; data-theme="dark" or "light" on <html> forces one. Agents learn this from the Pages skill.
How agents use widgets
An agent building a page over a Bases view looks for a widget, writes a tag, and ships:<ravi-view>lists a view’s records as a list, board, table or gallery. Withoutwidget, it uses the view’s default widget, or the default card.<ravi-form>is a form that creates a record through the view.<ravi-widget>draws one card from fixed values indata: the same numbers for everyone, never live rows.
ravi.bases.* actions the tags need to the page’s --uses. A required field that matches no column, or a tag that is not closed, stops the ship (exit 2) with the file, line and column, and nothing is published. The tags follow the same rules as any data page: signed-in members only, on private or protected_link routes.
To make a widget the default for every page over a view (board, list, gallery or calendar layouts):
bind applies at once and needs permission to manage the base. --field maps a widget field to a column when their names differ.
Make a widget
Make one when the same card keeps showing up across pages, or when you want a record to look a particular way everywhere.widget.json (name, description and typed fields), widget.html (the template), and optionally widget.js (see Widgets with code), example.json (made-up records for previews) and README.md. The template uses only Ravi’s building blocks, such as cards, rows, titles, fields, badges, images, links, metrics, progress bars and buttons:
ravi widgets catalog lists every building block and option. Agents propose widgets (ravi widgets propose ./order-card --scope org --execute); people publish them. An agent proposes to the organization or to its own Console project; when it names another project, the refusal (LIBRARY_OUT_OF_VIEW) says which organization and project the session sees. init --from-view reads the view in the session’s Console project; when the view is not found, the error names that project, and --project <slug> picks another.
Widgets with code
When a template cannot draw what you need, such as a chart or a map, a widget can add awidget.js file. The template stays required: it is what the ship draws into the HTML, and what agents and readers without JavaScript get. The script only enhances the card on top of it.
widget.jsis one ES module of at most 128 KiB with its libraries bundled in. It cannot import anything, load remote scripts or start workers.- It exports
mount(card, ctx)and returns{ update, destroy }.ctxcarries the card’s record and fields,canvas(i), the locale, time zone and light or dark mode,theme(name)for theme variables,announce(text)for screen readers, andonModeChange(fn). - The script draws only inside
<ravi-canvas label="Sales by month">, the one free-drawing area, which needs alabeland exists only in widgets with awidget.js. - Widgets with code are read-only for now: buttons in the template keep working, but the script cannot change records.
- Only an organization owner or admin can publish a widget with code; agents and developers propose it. The Console runs an accessibility audit of the card with its script over
example.json, and a page that uses a version whose audit has not passed does not ship:ravi pages shipexits 2 withKIT_WIDGET_UNAUDITEDand the Console link where an admin runs the audit.
ravi widgets catalog marks these widgets “com código” (script: true in --json).
Accessibility
Accessibility is built into the building blocks and checked when a theme or widget is published, so a page does not depend on each author remembering it.For people
- A theme is refused when a text and background pair falls below 4.5:1, or a control border, focus ring or accent below 3:1, in light or dark mode, including each option color.
- Every record is a titled card; status always shows its name, not just a color; buttons are real buttons named after the action and the record (“Mark as done: Order 1042”).
- Everything works from the keyboard, with a visible focus ring in the theme’s color. On a board, cards move with a “Move to…” menu as well as by dragging, and Esc closes menus and dialogs.
- Loading, saving, conflicts and errors are announced to screen readers.
- Reduced motion, high-contrast (forced colors) mode, 200% zoom and 24 px touch targets are respected.
For agents
- Page Chat, browser agents and
ravi pages readread the same accessibility tree people’s screen readers do. - Every record carries
data-ravi-row, and every fielddata-ravi-fieldanddata-ravi-valuewith the raw value, so agents do not have to parse formatted text. - Cards with fixed values are drawn into the HTML when the page ships, so readers that do not run JavaScript still see them.
alt text, an avatar has no name, a button or link has no text, a color or tone is the only signal, a heading level is skipped, or a card has no title.
Limits worth knowing
An older Console without widget support ships the HTML unchanged with the warning
KIT_UNAVAILABLE, and the tags do not render. Every other warning the Console sends on a ship (for example KIT_THEME_DEFAULT or KIT_ACTIONS_NEED_VIEW) is printed with its code and message and listed in warnings; warnings never stop the ship.