From sidemantic
Build analytics webapps, dashboards, or embedded UIs around Sidemantic semantic models using copyable React/Tailwind or static component primitives and deterministic query inspection.
How this skill is triggered — by the user, by Claude, or both
Slash command
/sidemantic:webapp-builderThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Build webapps around a validated Sidemantic semantic layer. Default to project-owned source components copied from this skill, then adapt them to the target app and wire them to inspected Sidemantic query contracts.
agents/openai.yamlassets/components/react-tailwind/column-chart.tsxassets/components/react-tailwind/dashboard-shell.tsxassets/components/react-tailwind/data-preview-table.tsxassets/components/react-tailwind/filter-pill.tsxassets/components/react-tailwind/index.tsassets/components/react-tailwind/leaderboard.tsxassets/components/react-tailwind/metric-card.tsxassets/components/react-tailwind/query-debug-panel.tsxassets/components/react-tailwind/sparkline.tsxassets/components/react-tailwind/states.tsxassets/components/react-tailwind/types.tsassets/components/static/sidemantic-components.cssassets/components/static/sidemantic-components.jsassets/templates/static-dashboard/app.jsassets/templates/static-dashboard/index.htmlreferences/webapp-patterns.mdscripts/copy_components.pyscripts/inspect_layer.pyscripts/scaffold_static_app.pyBuild webapps around a validated Sidemantic semantic layer. Default to project-owned source components copied from this skill, then adapt them to the target app and wire them to inspected Sidemantic query contracts.
In command examples, set SIDEMANTIC_PLUGIN_ROOT to the installed sidemantic plugin directory.
Treat assets/components/ like a small shadcn-style source library for analytics primitives. Copy components into the target project, then edit those copied files as normal app code. Do not retype component source into the answer or keep it as a hidden runtime dependency.
Copy React + Tailwind components for product apps:
uv run ${SIDEMANTIC_PLUGIN_ROOT}/skills/webapp-builder/scripts/copy_components.py \
--kind react-tailwind \
--target src/components/sidemantic
Copy the static component files for plain HTML demos or generated scaffolds:
uv run ${SIDEMANTIC_PLUGIN_ROOT}/skills/webapp-builder/scripts/copy_components.py \
--kind static \
--target public/sidemantic-components
Use --component metric-card --component leaderboard to copy a narrower React subset. Use --list before copying when you need the available names. Existing target files are never overwritten unless --force is passed.
Available React primitives:
DashboardShell: dense analytics page frame with status and toolbar slots.MetricCard: metric label, value, delta, loading, selected state.Leaderboard: ranked dimension rows with bars, selection, and stable data attributes.FilterPill: active filter display and removal.Sparkline: small SVG trend line.ColumnChart: compact categorical bars for comparisons.QueryDebugPanel: generated SQL/debug surface.DataPreviewTable: stable sample row preview.LoadingState, EmptyState, ErrorState: fixed-height status surfaces.State primitives are conditional UI branches. Do not render loading, empty, and error examples as permanent app content unless the user explicitly asks for a component gallery.
Available static JS helpers in sidemantic-components.js:
aliasForSemanticRef, toComponentResult, toComponentQuery, filterZeroMetricRows.labelize, formatValue, metricConfigFor, metricValueFormat, formatDateLike, seriesRangeLabel.renderMetricCards, renderMetricSummaryCards, renderLeaderboard, renderDimensionLeaderboardCards, renderFilterPills, renderSparkline, renderColumnChart, renderHighlightedQueryDebug, renderDataPreview, renderValidationState, renderState.renderSelectOptions, setControlsDisabled, syncScrollPosition.normalizeFilterValue, toggleFilterValue, removeFilterValue, removeFilterDimension.Use those static helpers before writing one-off DOM wiring. They are intentionally generic: they work for metric explorers, focused dashboards, debug tools, and browser-only demos as long as the app passes semantic query metadata plus row results into them. Runtime adapters for Python APIs, Pyodide, Rust WASM, DuckDB-WASM, or product backends should stay outside the copied component files.
info and the inspector in noninteractive agent work. Use validate only when the current CLI exits cleanly in your environment:uv run sidemantic info path/to/models
uv run ${SIDEMANTIC_PLUGIN_ROOT}/skills/webapp-builder/scripts/inspect_layer.py path/to/models \
--db path/to/data.duckdb \
--require-execute
uv run ${SIDEMANTIC_PLUGIN_ROOT}/skills/webapp-builder/scripts/inspect_layer.py path/to/models \
--db path/to/data.duckdb \
--require-execute \
--output docs/sidemantic-app-spec.json
Use --leaderboard-dimension field_name when domain judgment says one dimension should drive the first leaderboard. Without it, the inspector prefers common categorical dimensions over identifiers and booleans.
uv run ${SIDEMANTIC_PLUGIN_ROOT}/skills/webapp-builder/scripts/copy_components.py \
--kind react-tailwind \
--target src/components/sidemantic
Adapt imports, class names, and styling conventions after copying. Preserve the data contract conventions: data-metric, data-dimension, data-value, and data-testid hooks for metric totals, dimension leaderboards, and query debug surfaces.
For a minimal static app scaffold from the executed spec:
uv run ${SIDEMANTIC_PLUGIN_ROOT}/skills/webapp-builder/scripts/scaffold_static_app.py \
docs/sidemantic-app-spec.json \
--output dist/sidemantic-dashboard \
--title "Metrics Dashboard"
The scaffold copies readable source from assets/templates/static-dashboard/ and the static component files. If you need a richer generated app, edit those copied source files in the target project; do not bury application JavaScript in Python strings or generated HTML fragments.
sidemantic.api_server.create_app() or start_api_server() when a FastAPI API is acceptable.sidemantic-wasm npm package (Sidemantic Rust WASM) + DuckDB-WASM, or Pyodide + DuckDB-WASM, only for static demos or docs pages that must run without a backend.sidemantic.widget.MetricsExplorer instead of rebuilding the widget.sidemantic mcp-serve --apps --http --port 4100 and existing chart resources when the target is an MCP Apps-compatible host.{
"metrics": ["orders.revenue"],
"dimensions": ["orders.order_date__day"],
"filters": ["orders.status = 'completed'"],
"order_by": ["orders.order_date__day"],
"limit": 500
}
If a control is visible, it must change the app state or data. Do not satisfy interaction requirements by only changing a status label. Removing a filter must recompute metric cards, leaderboards, charts, and preview rows. Clicking a leaderboard row must add or toggle a filter. Selecting a metric must change the leaderboard ranking metric.
uv run sidemantic info path/to/models
uv run ${SIDEMANTIC_PLUGIN_ROOT}/skills/webapp-builder/scripts/inspect_layer.py path/to/models --db path/to/data.duckdb --require-execute
uv run sidemantic query "SELECT metric_name FROM model_name LIMIT 5" --models path/to/models --db path/to/data.duckdb
uv run ${SIDEMANTIC_PLUGIN_ROOT}/skills/webapp-builder/scripts/verify_static_app.py dist/sidemantic-dashboard
bunx --bun -p playwright node ${SIDEMANTIC_PLUGIN_ROOT}/skills/webapp-builder/scripts/verify_static_interactions.mjs --url http://127.0.0.1:5174/
bun run build
For frontend changes, run the app on a 4xxx-5xxx port and verify with browser screenshots at desktop and mobile widths. If browser tooling is unavailable, run verify_static_app.py or another deterministic DOM/data check and state that real browser visual verification was not run.
For interactive dashboards, verify behavior, not just render counts:
For static dashboards that use the bundled component contracts, use the smoke-test script after starting a local server:
bunx --bun -p playwright playwright install chromium # first run only, if Playwright reports a missing browser
bunx --bun -p playwright node ${SIDEMANTIC_PLUGIN_ROOT}/skills/webapp-builder/scripts/verify_static_interactions.mjs \
--url http://127.0.0.1:4519/
The script clicks filter pills, leaderboard rows, metric cards, and reset controls, and fails if visible data does not change.
Use the generated app spec first. For deeper implementation details, read references/webapp-patterns.md.
Default query set:
model.time_dimension__grain, ordered by time, capped around 500 points.Use inspect_layer.py --require-execute when a database is available. This adds result.columns, result.sample_rows, and sample_row_count to each compiled query and exits nonzero if execution is missing or fails. Use plain --execute only when a warning is acceptable.
For crossfilter leaderboards, exclude the dimension's own filter while querying that same dimension. If device_os = iOS is active, the device OS card should still show peer OS values while other cards show values within iOS.
Use explicit aliases at API boundaries so UI column names stay stable. The inspector emits output_aliases and, with --execute, actual result columns. Do not make display components depend on database-specific column casing or quoted identifiers.
Run DuckDB validation serially against a file database. Do not run the inspector and sidemantic query concurrently against the same .duckdb path; DuckDB file locks can make valid workflows fail.
When the app can call Python directly, prefer the existing HTTP API:
GET /healthGET /modelsGET /graphPOST /compilePOST /query?format=jsonPOST /query?format=arrowPOST /sqlWhen building a TypeScript frontend with a separate backend, keep Sidemantic execution in Python unless the project already has a stable Python service. A Hono server can proxy to the Sidemantic API, add auth/session context, and normalize responses.
Never concatenate user-entered filter values into SQL in the frontend. Pass structured filter values to a server-side query builder or quote them with the same rules as Sidemantic/widget code.
The CLI sidemantic query auto-adds default time dimensions for metrics when a model has default_time_dimension. For exact app query shapes like true totals, prefer the inspector-generated SQL/result samples or the Python/API structured query path using skip_default_time_dimensions=True internally.
scripts/inspect_layer.py: inspect models, compile app query shapes, execute samples with --execute, or require execution with --require-execute.scripts/copy_components.py: copy React + Tailwind or static component source from assets/components/ into a project.scripts/scaffold_static_app.py: create a small static dashboard from an executed app spec by copying templates and components. It writes index.html, styles.css, sidemantic-components.js, app.js, and data/app-spec.json.scripts/verify_static_app.py: dependency-free fallback verifier for static dashboards. It checks files, executed result samples, true totals, non-id leaderboard dimensions, and expected DOM/data bindings.scripts/verify_static_interactions.mjs: Playwright smoke test for standard static component contracts. It verifies real data changes for filter, leaderboard, metric, reset, and chart-bounds behavior.assets/components/react-tailwind/: copyable React source for analytics apps using Tailwind v3.assets/components/static/: copyable plain JS/CSS components and helper utilities for generated demos and no-build static pages.assets/templates/static-dashboard/: readable static app templates used by scaffold_static_app.py.After copying assets into a project, treat them as that project's code. Modify them to match local component APIs, naming, tests, and design system constraints.
Use browser-only Sidemantic execution for static demos, docs, and shareable examples. The UI should still consume the copied static component files; keep runtime adapters separate from components.
sidemantic-wasm package (bun add sidemantic-wasm), call createSidemanticRuntime(), compile/validate/rewrite through it, and keep the generated SQL visible in the debug panel. For a no-bundler static page, vendor the package's bundle (see examples/sidemantic_wasm_demo).Analytics webapps should feel work-focused:
sidemantic validate requires textual or opens a TUI, use sidemantic info plus inspect_layer.py as the noninteractive check.inspect_layer.py --require-execute when possible so result columns and sample rows are checked and failures are nonzero.viewBox, padding, and overflow: hidden; verify with screenshots.3000 or 8000 in worktrees. Prefer 4100, 4400, 5174, or another available 4xxx-5xxx port.file:// when it fetches JSON/CSV. Serve it locally instead, because browser file-scheme fetch behavior differs from a real app.npx claudepluginhub sidequery/sidemantic --plugin sidemanticBuilds apps on Databricks Apps platform for dashboards, data apps, analytics tools, and visualizations. Evaluates analytics vs Lakebase data access patterns before scaffolding.
Builds data dashboards and interactive reports using React + Vite or Streamlit, with optional Gemini Data Analytics chat integration for GCP data sources.
Build, validate, and manage semantic models using Sidemantic. Creates semantic layers mapping database tables to business dimensions/metrics, generates SQL, and imports from Cube/dbt/LookML.