From revel-webapp
Scaffold, build, and deploy full-screen Revel Digital webapps using the @reveldigital/client-sdk. Use this skill whenever the user wants to create a Revel Digital webapp, a digital signage app or screen, full-screen signage content, a kiosk or display app, or mentions deploying a web app to the Revel Digital CMS / player via the client SDK. Bakes in a theme system, Section 508 / WCAG accessibility, and distance readability. Covers React, Vue, Angular, and Vanilla JS. Can deploy straight from Claude Code via the Revel Digital MCP server (import_media) or via the RevelDigital/webapp-action GitHub Action. Use the separate revel-gadget skill instead when the user wants an embeddable gadget/widget defined by gadget.yaml and hosted on GitHub Pages.
How this skill is triggered — by the user, by Claude, or both
Slash command
/revel-webapp:revel-webappThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
This skill scaffolds a complete **Revel Digital webapp** — a self-contained, full-screen HTML5
This skill scaffolds a complete Revel Digital webapp — a self-contained, full-screen HTML5
application that runs on a Revel Digital player and communicates with the player through the
@reveldigital/client-sdk shim. The scaffolded project is signage-ready out of the box: it ships
with a theme system, accessibility (Section 508 / WCAG 2.1 AA), distance-readable typography, and a
one-step deploy to the Revel Digital CMS.
A webapp and a gadget are different deliverables. This skill builds webapps.
| Webapp (this skill) | Gadget (revel-gadget skill) | |
|---|---|---|
| Runs as | Full-screen app on the player | Embedded widget inside Revel content |
| Definition file | None — content driven by config.json | gadget.yaml |
| Build output | dist/ (static site) | dist/ + generated gadget XML |
| Packaged as | <name>.webapp (zip) | gadget XML descriptor |
| Deployed to | Revel Digital CMS via webapp-action | GitHub Pages via gadgetizer |
Do NOT generate gadget.yaml, run gadgetizer, add a build:gadget script, or deploy to
GitHub Pages for a webapp. If the user actually wants a gadget, point them at the revel-gadget
skill instead.
Before scaffolding, gather:
@reveldigital/player-client (Angular-native, DI & RxJS)package.json name (the deploy action reads
the webapp name from here).prefers-color-scheme). These feed the theme tokens in
theme.css.references/deploy.md (Option A)..github/workflows/deploy.yml for hands-off redeploy on every
git push (needs a repo + Revel_API_Key secret).Always read three files: the chosen framework file plus signage.md and deploy.md.
| Framework | Reference file |
|---|---|
| React | references/react.md |
| Vue | references/vue.md |
| Angular | references/angular.md |
| Vanilla JS | references/vanilla.md |
| Always also read | Purpose |
|---|---|
references/signage.md | Theme tokens, accessibility (508/WCAG), readability — baked into every app |
references/deploy.md | CMS deploy workflow (webapp-action) + advisory quality report |
Follow the reference files precisely — they contain the exact file structure, dependencies, and sample code.
Generate every file listed in the framework reference, complete (no elided snippets). The
project must npm install and npm run build cleanly to a dist/ folder containing index.html
— that dist/ folder is exactly what the deploy action uploads.
Apply references/signage.md to every scaffold:
theme.css with the design tokens and import it globally. Recolor --brand from the user's
answer; set the default data-theme / color-scheme per their light/dark/auto choice.<main>, <header>), set <html lang> from getLanguageCode(), add
visible focus styles, and guard animations with prefers-reduced-motion.clamp() type scale and the overscan-safe --safe-area padding on the root layout.The scaffolded app should be an immediately useful signage starting point that demonstrates the SDK:
createPlayerClient(), or inject PlayerClientService for Angular).getDeviceTime() (re-read on an interval; respect the device
timezone).getDevice() (name, city/state from location).EventType.COMMAND (React/Vue/Vanilla) or onCommand$
(Angular) and react to a sample command.isPreviewMode() — fall back gracefully so the app renders in a plain browser during
development (wrap every async SDK call in .catch()).config.json rather than
hard-coded values — this is the webapp equivalent of gadget preferences.Drive content from data, not hard-coded values:
config.json (above).getDevice().location (lat/long) for geo-aware content.createDataTable() shim is gadget-only. The
player does not inject the data-table library into a full-screen webapp, so calling it there
throws. In a webapp, source data-table content one of two ways:
list_datatable_rows
(or GET /datatables/{id}/rows) and write the rows into a bundled JSON (e.g. tenants.json) the
app fetches at runtime. No secret in the artifact; refresh by re-baking + redeploying. (MCP row
writes need each row shaped { "data": { …values… } }.)GET /datatables/{id}/rows?api_key=… from the app for live data, but
this embeds a scoped read key in the client artifact — only acceptable on trusted players.createDataTable().getRows() + on('rowUpdated', …)),
build a gadget (revel-gadget skill) instead of a webapp.See references/deploy.md for all three options. Default to the MCP method — it's the lowest
overhead (no CI, no secret, no files added) and works in any MCP-capable, skill-capable host.
Via the Revel Digital MCP server (recommended) — deploy without CI, directly in the
conversation; OAuth (browser login), so no API key is needed. Connect first (Claude Code:
claude mcp add --transport http reveldigital https://mcp.reveldigital.io/mcp; other hosts: add
the same endpoint in their MCP config). Detailed in references/deploy.md (Option A):
npm run build → zip the build folder
contents into <name>.webapp → create_media_upload({ name, content_type, group_id? }) (MCP,
OAuth) → curl -T <name>.webapp -H "Content-Type: …" "<upload_url>" (local, straight to S3, no
auth) → finalize_media_upload({ id }) (MCP, OAuth). The binary never passes through the MCP
server or the model. Requires the server's upload proxy tools; if absent, the same three
PublicAPI endpoints (POST /media/uploads, PUT <upload_url>, POST /media/uploads/{id}/finalize)
can be called directly with an X-RevelDigital-ApiKey. Confirm the finalized record is
webapp-typed: mime_type = application/x-reveldigital-webapp.gh) and call import_media({ url, group_id? }). Use when presigned upload isn't
available.GitHub Action (CI) — only when the user wants automated redeploy on push. Create
.github/workflows/deploy.yml that checks out, sets up Node, npm ci, npm run build, then calls
RevelDigital/webapp-action@v1 with api-key: ${{ secrets.Revel_API_Key }}. Include the
advisory accessibility/performance report step (continue-on-error: true, writes to
$GITHUB_STEP_SUMMARY) — it never fails the build. Tell the user to add the Revel_API_Key
repository secret.
Direct REST upload — a one-off local curl multipart upload (binary, API key, no MCP, no CI).
See references/deploy.md (Option C).
npm install
npm run dev # develop in the browser (SDK calls fall back gracefully off-device)
npm run build # production build to ./dist
To deploy: push to GitHub with the Revel_API_Key secret set, or run the build and upload dist/
via RevelDigital/webapp-action. The webapp then appears in the Revel Digital CMS media library and
can be scheduled to players.
Applies to @reveldigital/client-sdk. Angular uses @reveldigital/player-client — same methods
but RxJS observables for lifecycle events; see references/angular.md.
import { createPlayerClient, EventType } from "@reveldigital/client-sdk";
const client = createPlayerClient();
// Events — handle player lifecycle and commands
client.on(EventType.START, () => { /* player started */ });
client.on(EventType.STOP, () => { /* player stopped */ });
client.on(EventType.COMMAND, (data) => { /* command received: data.name, data.arg */ });
client.off(EventType.START); // remove listener (clean up on unmount)
// Device info (all Promise-based)
await client.getDeviceTime(); // device-local time, ISO8601 — use for the clock
await client.getDeviceTimeZoneName(); // e.g. "America/Chicago"
await client.getDeviceTimeZoneID();
await client.getDeviceTimeZoneOffset(); // minutes from GMT
await client.getLanguageCode(); // e.g. "en" — set on <html lang>
await client.getDeviceKey(); // unique device identifier
await client.getDevice(); // full IDevice: name, tags[], timeZone, location {…}
await client.isPreviewMode(); // true in CMS preview — fall back gracefully
// Display / content info
await client.getWidth(); // screen/zone width in pixels
await client.getHeight(); // screen/zone height in pixels
await client.getDuration(); // scheduled duration in ms
await client.getRevelRoot(); // Revel Digital root URL
await client.getCommandMap();
await client.getSdkVersion();
// Communication
client.sendCommand(name, arg); // send a command to the player
client.sendRemoteCommand(deviceKeys, name, arg); // send to other devices on the account
client.callback(...args);
// Analytics
client.track(eventName, properties);
client.timeEvent(eventName);
getDevice() resolves to an IDevice with name, registrationKey, deviceType, tags: string[],
langCode, timeZone, and an optional location →
{ latitude, longitude, city, state, address, country, postalCode }. Use location for
geo/timezone-aware content (local weather, store address, regional messaging).
null or throw — always wrap async SDK calls in .catch() and design sensible fallbacks
so npm run dev looks right.client.off(...) / unsubscribe) on unmount to avoid leaks.dist/ — no XML, no gadget descriptor. Keep asset paths
relative (base: './' in Vite) so the webapp loads regardless of where the player serves it from.name and version come from package.json; the deploy action reads them
automatically.import type { PlayerClient, IDevice, IEventProperties } from '@reveldigital/client-sdk';
(Angular: import from @reveldigital/player-client).Provides CDSS development patterns for drug interaction checking, dose validation, clinical scoring (NEWS2, qSOFA), and alert classification integrated into EMR workflows.
npx claudepluginhub reveldigital/reveldigital-webapp-skill --plugin revel-webapp