--- name: rill-canvas description: Detailed instructions and examples for developing canvas dashboard resources in Rill --- # Instructions for developing a canvas dashboard in Rill ## Introduction Canvas dashboards are free-form dashboard resources that display custom chart and table components laid out in a grid. They enable building overview and report-style dashboards with multiple visualizations, similar to traditional business intelligence tools. Canvas dashboards differ from explore dashboards in important ways: - **Explore dashboards:** Best for explorative analysis, drill-down investigations, and letting users freely slice data by any dimension. - **Canvas dashboards:** Best for fixed reports, executive summaries, or combining multiple metrics views into a single view. Canvas dashboards are lightweight resources found downstream of metrics views in the project DAG. Each component within a canvas fetches data individually, typically from a metrics view resource. **When to use canvas dashboards:** - Building executive summaries with KPIs and multiple visualizations - Creating report-style dashboards with markdown explanations - Comparing metrics across different metrics views - Designing custom layouts not possible with explore dashboards ## Canvas Structure A canvas dashboard is defined in a YAML file with `type: canvas`. Here is the basic structure (most canvas dashboards work great without any of the optional properties here): ```yaml type: canvas display_name: "Sales Overview Dashboard" # Optional filter settings filters: enable: true pinned: - region - product_category required: - customer_id # Dashboard refuses to render until this filter has a value # Optional time range presets time_ranges: - P7D - P30D - P90D - inf # Optional maximum dashboard width max_width: 1400 # Optional theme reference theme: my_theme # Default time settings for all components defaults: time_range: P7D comparison_mode: time # Optional default filters, keyed by metrics view name filters: sales_metrics: "region IN ('US', 'CA')" # Optional security access control security: access: "'{{ .user.domain }}' == 'company.com'" # Required dashboard content organized in rows rows: - height: 240px items: - width: 12 kpi_grid: metrics_view: sales_metrics measures: - total_revenue - order_count - height: 400px items: - width: 6 line_chart: metrics_view: sales_metrics title: "Revenue Trend" x: type: temporal field: event_time y: type: quantitative field: total_revenue - width: 6 bar_chart: metrics_view: sales_metrics title: "Revenue by Region" color: primary x: type: nominal field: region limit: 10 sort: -y y: type: quantitative field: total_revenue ``` ### Filter settings `filters.pinned` and `filters.required` both reference dimensions or measures by name (they must exist on at least one referenced metrics view). They control different things: - **`pinned`**: keeps a filter pill visible at the top of the dashboard so users can change it without opening the filter menu. The dashboard renders normally whether or not the user has set a value. - **`required`**: the dashboard refuses to render until every required filter has a value. Set a default value in `defaults.filters` to satisfy a required filter automatically; otherwise the user is prompted to pick one. Required filters are implicitly pinned — do not also list them under `pinned`. Use `required` for filters that are unsafe or meaningless to omit (e.g. tenant ID, customer scope, region when results would otherwise mix incompatible data). #### Default filters `defaults.filters` is a **map keyed by metrics view name**, where each value is a **Metrics SQL `WHERE` expression** applied when the dashboard first loads. ```yaml defaults: time_range: P7D filters: sales_metrics: "region IN ('US', 'CA') AND total_revenue > 1000" support_metrics: "ticket_status != 'closed'" ``` Rules for writing `defaults.filters`: - Each key must be the name of a metrics view that is referenced by at least one component on the canvas. - A filter only applies to the components backed by its metrics view. To filter several metrics views, add one entry per metrics view, including duplicating shared dimension/measure names. - Expressions reference dimension and measure **names as defined on that metrics view**, not the underlying table columns. Table prefixes, aliases, and subqueries are not supported. - Write only the expression itself; do not include the `WHERE` keyword. - Supported syntax: `=`, `!=`, `<`, `<=`, `>`, `>=`, `IN`, `NOT IN`, `LIKE`, `ILIKE`, `IS NULL`, `IS NOT NULL`, `BETWEEN`, combined with `AND`, `OR`, and parentheses. String literals use single quotes. ## Layout System Canvas dashboards use a 12-unit grid system for layout. ### Row Configuration Each row defines a horizontal section with a specific height: ```yaml rows: - height: 240px # Row height in pixels items: # Components go here ``` **Recommended row heights:** - Markdown headers: 40px - 80px - KPI grids: 128px - 240px (depending on number of measures) - Charts and visualizations: 300px - 500px - Leaderboards: 300px - 450px - Tables: 300px - 500px ### Item Widths Items within a row share the 12-unit width: ```yaml rows: # Full width (1 component per row) - items: - width: 12 markdown: content: "# Dashboard Title" # Half width (2 components per row) - items: - width: 6 line_chart: # ... - width: 6 bar_chart: # ... # Third width (3 components per row) - items: - width: 4 donut_chart: # ... - width: 4 bar_chart: # ... - width: 4 area_chart: # ... ``` **Width guidelines:** - `width: 12` - Full width; use for KPI grids, markdown headers, wide charts - `width: 6` - Half width; use for side-by-side comparisons - `width: 4` - Third width; use for three equal charts - `width: 3` - Quarter width; use for four small components (minimum practical width) ### Tab groups A row entry can be a tab group instead of a plain row. A tab group has a `name` (URL key) and a list of `tabs`, each with a `label`, optional `name`, and its own `rows`. Only the active tab loads data. Allowed only at the top level, not nested inside a tab. ```yaml rows: - name: by_region tabs: - label: North America rows: - items: - width: 6 bar_chart: # ... - label: EMEA rows: - items: - width: 6 bar_chart: # ... ``` ## Dashboard Composition Best Practices When building a new canvas dashboard, follow this recommended structure: 1. **Row 1 - Context**: Start with a markdown component providing dashboard title and overview 2. **Row 2 - Key Metrics**: Add a KPI grid with 2-4 of the most business-relevant measures 3. **Row 3 - Primary Analysis**: Split into two halves: - Left (width 6): A leaderboard showing top entities by a key dimension - Right (width 6): A time-series chart (line_chart or stacked_bar) showing trends 4. **Additional Rows**: Add 1-2 more rows with relevant charts based on the data **Choosing chart types:** - **Time-series analysis**: Use `line_chart` or `area_chart` with temporal x-axis - **Categorical comparisons**: Use `bar_chart` or `stacked_bar` with nominal x-axis - **Part-to-whole**: Use `donut_chart` or `stacked_bar_normalized` - **Two-dimensional patterns**: Use `heatmap` - **Dual-metric comparison**: Use `combo_chart` for two measures with different scales - **Funnel analysis**: Use `funnel_chart` to visualize sequential stage drop-offs - **Two-measure relationships**: Use `scatter_plot` to explore correlation, clusters, or outliers between two numeric measures # Field guidelines The field names are case sensitive and should match exactly to the fields present in the metrics view. **Time dimension restrictions:** The time dimension (timeseries field from the metrics view) is special and can ONLY be used in the x-axis field for temporal charts. Never use the time dimension in: - Leaderboard dimensions - Color fields - Any other dimension configuration ## Component Types ### Markdown Add text content, headers, and documentation: ```yaml markdown: content: | ## Dashboard Overview This dashboard tracks key sales metrics across all regions. --- alignment: horizontal: left # left, center, right vertical: middle # top, middle, bottom ``` **Best practices:** - Use markdown for dashboard titles, section headers, and explanatory text - Add blank lines between markdown elements for proper rendering - Use `---` for horizontal rules to separate sections ### KPI Grid Display key metrics with comparison values and sparklines: ```yaml kpi_grid: metrics_view: sales_metrics measures: - total_revenue - order_count - average_order_value - customer_count comparison: - delta # Absolute change - percent_change # Percentage change - previous # Previous period value sparkline: right # right, bottom, none ``` **With dimension filters:** ```yaml kpi_grid: metrics_view: sales_metrics measures: - total_revenue - order_count dimension_filters: region IN ('North America', 'Europe') comparison: - percent_change sparkline: bottom hide_time_range: true ``` ### Leaderboard Display ranked dimension values by measures: ```yaml leaderboard: metrics_view: sales_metrics title: "Top Products" description: "Products ranked by total revenue" dimensions: - product_category measures: - total_revenue - order_count num_rows: 10 ``` **With multiple dimensions:** ```yaml leaderboard: metrics_view: sales_metrics dimensions: - region - product_category measures: - total_revenue - average_order_value - order_count num_rows: 7 ``` **Important:** Never use time dimensions in leaderboard dimensions. Leaderboards are for categorical ranking, not time-series analysis. ### Line Chart Show trends over time: ```yaml line_chart: metrics_view: sales_metrics title: "Revenue Trend" color: primary x: field: order_date type: temporal limit: 30 y: field: total_revenue type: quantitative zeroBasedOrigin: true ``` **With color dimension breakdown:** ```yaml line_chart: metrics_view: sales_metrics title: "Revenue by Region" color: field: region type: nominal limit: 5 legendOrientation: top x: field: order_date type: temporal y: field: total_revenue type: quantitative zeroBasedOrigin: true ``` **With custom color mapping:** ```yaml line_chart: metrics_view: sales_metrics title: "Performance Comparison" color: field: status type: nominal colorMapping: - value: "active" color: hsl(120, 70%, 45%) - value: "inactive" color: hsl(0, 70%, 50%) x: field: event_date type: temporal y: field: event_count type: quantitative ``` ### Bar Chart Compare values across categories: ```yaml bar_chart: metrics_view: sales_metrics title: "Revenue by Product Category" color: hsl(210, 70%, 50%) x: field: product_category type: nominal limit: 10 sort: -y y: field: total_revenue type: quantitative zeroBasedOrigin: true ``` **With color dimension:** ```yaml bar_chart: metrics_view: sales_metrics title: "Revenue by Category and Region" color: field: region type: nominal limit: 5 x: field: product_category type: nominal limit: 8 sort: -y y: field: total_revenue type: quantitative ``` **Horizontal bars** (bars run left to right; useful for long category labels or many categories). `x` and `y` always name the field drawn on that axis, so put the measure on `x` and the dimension on `y`: ```yaml bar_chart: metrics_view: sales_metrics title: "Revenue by Product Category" color: primary x: field: total_revenue type: quantitative zeroBasedOrigin: true y: field: product_category type: nominal limit: 15 sort: -x ``` Sort values refer to axes, so `sort: -x` on `y` orders the categories by the measure. The same layout works for `stacked_bar` and `stacked_bar_normalized`; `line_chart` and `area_chart` always keep the dimension on `x`. ### Stacked Bar Show cumulative values across categories or time: ```yaml stacked_bar: metrics_view: sales_metrics title: "Revenue Over Time by Region" color: field: region type: nominal limit: 5 x: field: order_date type: temporal limit: 20 y: field: total_revenue type: quantitative zeroBasedOrigin: true ``` **With multiple measures:** ```yaml stacked_bar: metrics_view: sales_metrics title: "Cost Breakdown Over Time" color: field: rill_measures type: value legendOrientation: top x: field: order_date type: temporal limit: 20 y: field: cost_of_goods fields: - cost_of_goods - shipping_cost - marketing_cost type: quantitative zeroBasedOrigin: true ``` ### Stacked Bar Normalized Show proportional distribution (100% stacked): ```yaml stacked_bar_normalized: metrics_view: sales_metrics title: "Revenue Share by Region" color: field: region type: nominal limit: 5 x: field: order_date type: temporal limit: 20 y: field: total_revenue type: quantitative zeroBasedOrigin: true ``` **With custom color mapping for measures:** ```yaml stacked_bar_normalized: metrics_view: inventory_metrics title: "Inventory Status Distribution" color: field: rill_measures type: value legendOrientation: top colorMapping: - value: "in_stock" color: hsl(120, 60%, 50%) - value: "low_stock" color: hsl(45, 90%, 50%) - value: "out_of_stock" color: hsl(0, 70%, 50%) x: field: report_date type: temporal limit: 20 y: field: in_stock fields: - in_stock - low_stock - out_of_stock type: quantitative ``` ### Area Chart Show magnitude over time with optional stacking: ```yaml area_chart: metrics_view: sales_metrics title: "Order Volume Over Time" color: primary x: field: order_date type: temporal limit: 30 y: field: order_count type: quantitative zeroBasedOrigin: true ``` **With color dimension:** ```yaml area_chart: metrics_view: sales_metrics title: "Revenue by Channel" color: field: sales_channel type: nominal limit: 4 x: field: order_date type: temporal limit: 20 y: field: total_revenue type: quantitative zeroBasedOrigin: true ``` ### Donut Chart Show proportional breakdown: ```yaml donut_chart: metrics_view: sales_metrics title: "Revenue by Region" innerRadius: 50 color: field: region type: nominal limit: 8 sort: -measure measure: field: total_revenue type: quantitative showTotal: true ``` ### Heatmap Show patterns across two dimensions: ```yaml heatmap: metrics_view: activity_metrics title: "Activity by Day and Hour" color: field: event_count type: quantitative x: field: day_of_week type: nominal limit: 7 y: field: hour_of_day type: nominal limit: 24 sort: -color ``` **With custom color range:** ```yaml heatmap: metrics_view: performance_metrics title: "Performance Score Matrix" color: field: score type: quantitative colorRange: mode: scheme scheme: sequential x: field: category type: nominal limit: 10 y: field: subcategory type: nominal limit: 15 ``` **With custom Vega-Lite config for colors:** ```yaml heatmap: metrics_view: utilization_metrics title: "Resource Utilization" vl_config: | { "range": { "heatmap": ["#F4A261", "#D63946", "#457B9D"] } } color: field: utilization_rate type: quantitative x: field: resource_name type: nominal limit: 20 y: field: time_slot type: nominal limit: 12 ``` ### Combo Chart Combine bar and line on dual axes: ```yaml combo_chart: metrics_view: sales_metrics title: "Revenue and Order Count" color: field: measures type: value legendOrientation: top x: field: order_date type: temporal limit: 20 y1: field: total_revenue type: quantitative mark: bar zeroBasedOrigin: true y2: field: order_count type: quantitative mark: line zeroBasedOrigin: true ``` **With custom color mapping:** ```yaml combo_chart: metrics_view: funnel_metrics title: "Conversions and Conversion Rate" color: field: measures type: value legendOrientation: top colorMapping: - value: "Conversions" color: hsl(210, 100%, 73%) - value: "Conversion Rate" color: hsl(280, 70%, 55%) x: field: event_date type: temporal limit: 30 y1: field: conversions type: quantitative mark: bar y2: field: conversion_rate type: quantitative mark: line ``` ### Scatter Plot Visualize the relationship between two measures, with each point representing a data value. Points can be optionally colored by a dimension and sized by a measure. Useful for correlation analysis and identifying outliers across two numeric metrics. **Interactive controls** (rendered automatically): - `Shift + Drag`: pan the plot - Scroll wheel: zoom in/out - Double-click: reset zoom and pan to default view ```yaml scatter_plot: metrics_view: bids_metrics title: "Bids vs Clicks by Advertiser" x: field: total_bids type: quantitative zeroBasedOrigin: false y: field: total_clicks type: quantitative zeroBasedOrigin: false dimension: field: advertiser_name limit: 10 type: nominal size: field: total_impressions type: quantitative color: field: device_os limit: 5 type: nominal legendOrientation: top ``` **Scatter-specific fields:** - `x` (required): Measure on the x-axis. Must be `type: quantitative`. - `y` (required): Measure on the y-axis. Must be `type: quantitative`. - `dimension` (optional): Nominal dimension; each distinct value renders as a separate point. Use `limit` to cap the number of points. - `size` (optional): Measure that controls point radius. Must be `type: quantitative`. - `color` (optional): A color string or a nominal dimension field object to color points by. **Notes:** - Setting `zeroBasedOrigin: false` on both axes is usually preferred for scatter plots, since the interesting range often does not include zero. - `comparison_time_range` is not supported for scatter plots. ### Funnel Chart Show flow through stages or conversion processes: ```yaml funnel_chart: metrics_view: conversion_metrics title: "Conversion Funnel" breakdownMode: dimension color: stage mode: width percentMode: top stage: field: funnel_stage type: nominal limit: 10 measure: field: user_count type: quantitative ``` **With multiple measures breakdown and step-to-step percentages:** ```yaml funnel_chart: metrics_view: engagement_metrics title: "Engagement Funnel" breakdownMode: measures color: value mode: width percentMode: previous measure: field: impressions type: quantitative fields: - impressions - clicks - signups - purchases ``` **Breakdown modes and color options:** - `breakdownMode: dimension` with `color: stage` (different colors per stage) or `color: measure` (similar colors by value) - `breakdownMode: measures` with `color: name` (different colors per measure) or `color: value` (similar colors by value) **Percent mode (`percentMode`):** - `top` (default): the on-bar percentage label is each stage's value as a share of the top stage. Use for overall conversion ("how many of the entry-point users reached this stage"). - `previous`: the on-bar percentage label is each stage's value as a share of the immediately prior stage. Use for stage-to-stage drop-off ("what fraction of the previous stage continued"). - The tooltip always shows both `% of top` and `% of previous` regardless of this setting, so users can disambiguate without the author surfacing both. ### Pivot Create pivot tables with row and column dimensions: ```yaml pivot: metrics_view: sales_metrics title: "Sales by Region and Category" row_dimensions: - region - product_category col_dimensions: - quarter measures: - total_revenue - order_count - average_order_value hide_totals_row: false hide_totals_col: false ``` **With column widths, wrapping, and an initial sort:** Any entry of `measures`, `row_dimensions`, or `col_dimensions` can be an object with the field `name` plus overrides. In a pivot only measures and the first row dimension render a column of their own: a measure's `width` applies under every column-dimension value (as do `delta: { width }` and `percent_change: { width }` for its comparison columns), and `col_dimensions` entries accept only `name` and `label` (set widths on the measures instead). ```yaml pivot: metrics_view: sales_metrics fit_to_width: true # shrink unconfigured columns to fit instead of scrolling wrap_headers: true # wrap header labels over `wrap_lines` lines (default 2) sort_by: total_revenue # a measure (by row total) or a row dimension sort_dir: desc row_dimensions: - name: region width: 220 wrap: true - product_category col_dimensions: - name: quarter label: Quarter measures: - name: total_revenue width: 120 format_preset: currency_usd - name: order_count format_d3: ".3s" align: center ``` **Simple pivot (rows only):** ```yaml pivot: metrics_view: sales_metrics row_dimensions: - region col_dimensions: [] measures: - total_revenue - order_count - margin_rate ``` ### Table Display tabular data with specified columns: ```yaml table: metrics_view: sales_metrics title: "Product Performance" description: "Detailed breakdown of product metrics" columns: - product_name - product_category - total_revenue - order_count - average_price hide_totals_row: false ``` **With column widths, wrapping, and an initial sort:** Any entry of `columns` can be an object with the field `name` plus overrides: `width` (pixels; measures 60–300, dimensions 100–600), `wrap` (dimension columns), `align` (`left`, `center`, `right`), `label`, and for measures `format_preset` or `format_d3`, plus `delta: { width }` or `percent_change: { width }` for the comparison columns a metrics view measure gets while a time comparison is shown. Component-level `fit_to_width`, `wrap`, `wrap_headers`, `wrap_lines`, `sort_by`, and `sort_dir` apply to the whole table; `sort_comparison: delta` or `percent_change` sorts on a measure's comparison column while a time comparison is shown. ```yaml table: metrics_view: sales_metrics title: "Product Performance" fit_to_width: true wrap: true wrap_lines: 2 sort_by: total_revenue sort_dir: desc columns: - name: product_name width: 260 label: Product - product_category - name: total_revenue width: 120 format_preset: currency_usd - name: order_count format_d3: ".3s" align: center ``` **With dimension filters:** ```yaml table: metrics_view: sales_metrics title: "North America Sales" columns: - product_name - total_revenue - order_count dimension_filters: region IN ('North America') ``` ### Image Display external images. Set `dark_url` to show a different image when the dashboard is in dark mode (falls back to `url`): ```yaml image: url: https://example.com/logo.png dark_url: https://example.com/logo-dark.png alignment: horizontal: center vertical: middle ``` ### Custom Chart Build fully custom visualizations using Metrics SQL queries and Vega-Lite specifications. Use this when the built-in chart types are insufficient and you need complete control over the visualization. **Prefer a standalone component over an inline block.** Instead of embedding the `custom_chart` block in the canvas, create a `type: component` file under `viz_library/` that declares typed params, and reference it from the canvas item with `component: ` plus a `params:` map. This keeps canvas files small and makes the visualization reusable across dashboards. Note that component files describe charts with a chart spec under `spec` rather than with `vega_spec`, switching to `vega_spec` only when the chart spec cannot express the chart: the format below applies to inline blocks, and to an ejected component with the addition of param templating. See the component resource instructions for the file format, param types, and templating conventions. Use an inline `custom_chart` block only for trivial one-off charts. Example reference: ```yaml rows: - items: - component: measure_trend params: metrics_view: bids_metrics measure: total_bids time_dim: __time width: 8 ``` Custom charts use `metrics_sql` to query data from metrics views and `vega_spec` to define the Vega-Lite visualization. The data from each query is available in the Vega-Lite spec as named datasets: `query1`, `query2`, etc. #### metrics_sql Query Language `metrics_sql` lets you write SELECT queries against metrics views as virtual tables. Each metrics view exposes its dimensions and measures as columns. **Query rules:** - Table names: use the name of any valid metrics view in the project - Columns: only reference dimension and measure names defined in that metrics view's schema - Measures are pre-aggregated; never wrap them in `SUM()`, `COUNT()`, `AVG()`, or other aggregate functions - Grouping is implicit by selected dimensions; you do not need `GROUP BY` unless combining with expressions like `date_trunc()` - Use `date_trunc('', )` for time bucketing (grain: minute, hour, day, week, month, quarter, year) - Always include `ORDER BY` for deterministic results - Use `LIMIT` to keep result sets reasonable (default to 50 for top-N queries, 500 for time series) - Do not alias column names unless strictly required for disambiguation in multi-query specs - Canvas-level time and dimension filters are injected automatically at runtime; do not add `WHERE` clauses for them - You may write multiple queries against different metrics views; results are bound as `query1`, `query2`, etc. **Example queries:** Single view: ```sql SELECT publisher, total_bids, bid_price FROM bids_metrics ORDER BY total_bids DESC LIMIT 20 ``` Time series: ```sql SELECT date_trunc('day', __time) as day, impressions, revenue FROM ad_metrics ORDER BY day ``` Cross-view (two queries): ```sql -- query1: SELECT campaign, spend FROM spend_metrics ORDER BY spend DESC LIMIT 10 -- query2: SELECT campaign, conversions FROM conversion_metrics ORDER BY conversions DESC LIMIT 10 ``` #### Vega-Lite Specification Rules - Generate a valid Vega-Lite v5 JSON specification - Bind data with `{"name": "query1"}`, `{"name": "query2"}`, etc. Do not include `"data": {"values": [...]}` sections; data comes from query results - Set `"width": "container"` and `"height": "container"` so the chart fills its parent - Always include `"autosize": {"type": "fit"}` at the top level of the spec - Use `display_name` values from the metrics view schema for axis titles, legend titles, and tooltip labels - Apply `format_d3` or `format_preset` from measure metadata to axis and tooltip format strings - Pick the best mark type for the data: bar, line, area, point, rect (heatmap), arc (pie/donut), etc. - Include tooltips with all relevant fields and human-readable formatting - Use a clean, professional color scheme; prefer Rill's categorical palette when possible - For temporal axes: set `"type": "temporal"` and choose an appropriate `timeUnit` - For categorical axes: sort by the primary measure descending unless the user specifies otherwise - For layered or multi-view charts, use the `"layer"` or `"concat"` composition operators - Avoid unnecessary chart junk: remove gridlines on categorical axes, use concise axis labels #### YAML Examples **Simple custom chart with one query:** ```yaml custom_chart: metrics_sql: - | SELECT publisher, total_bids, bid_price FROM bids_metrics ORDER BY total_bids DESC LIMIT 20 vega_spec: | { "$schema": "https://vega.github.io/schema/vega-lite/v5.json", "width": "container", "height": "container", "autosize": {"type": "fit"}, "data": {"name": "query1"}, "mark": "bar", "encoding": { "x": {"field": "publisher", "type": "nominal", "sort": "-y", "axis": {"labelAngle": -45}}, "y": {"field": "total_bids", "type": "quantitative", "title": "Total Bids"}, "tooltip": [ {"field": "publisher", "type": "nominal"}, {"field": "total_bids", "type": "quantitative", "title": "Total Bids"}, {"field": "bid_price", "type": "quantitative", "title": "Bid Price", "format": ",.2f"} ] } } ``` **Time series custom chart:** ```yaml custom_chart: metrics_sql: - | SELECT date_trunc('day', __time) as day, impressions, revenue FROM ad_metrics ORDER BY day LIMIT 500 vega_spec: | { "$schema": "https://vega.github.io/schema/vega-lite/v5.json", "width": "container", "height": "container", "autosize": {"type": "fit"}, "data": {"name": "query1"}, "layer": [ { "mark": {"type": "line", "color": "#4C78A8"}, "encoding": { "x": {"field": "day", "type": "temporal", "title": "Date"}, "y": {"field": "impressions", "type": "quantitative", "title": "Impressions"} } } ], "encoding": { "tooltip": [ {"field": "day", "type": "temporal", "title": "Date"}, {"field": "impressions", "type": "quantitative", "title": "Impressions"}, {"field": "revenue", "type": "quantitative", "title": "Revenue", "format": "$,.2f"} ] } } ``` **Custom chart with multiple queries (cross-view):** ```yaml custom_chart: metrics_sql: - | SELECT campaign, spend FROM spend_metrics ORDER BY spend DESC LIMIT 10 - | SELECT campaign, conversions FROM conversion_metrics ORDER BY conversions DESC LIMIT 10 vega_spec: | { "$schema": "https://vega.github.io/schema/vega-lite/v5.json", "width": "container", "height": "container", "autosize": {"type": "fit"}, "layer": [ { "data": {"name": "query1"}, "mark": {"type": "bar", "opacity": 0.7}, "encoding": { "x": {"field": "campaign", "type": "nominal", "sort": "-y"}, "y": {"field": "spend", "type": "quantitative", "title": "Spend"}, "color": {"datum": "Spend"} } }, { "data": {"name": "query2"}, "mark": {"type": "line", "point": true}, "encoding": { "x": {"field": "campaign", "type": "nominal"}, "y": {"field": "conversions", "type": "quantitative", "title": "Conversions", "axis": {"orient": "right"}}, "color": {"datum": "Conversions"} } } ] } ``` **Additional guidelines:** - The `vega_spec` field must contain valid JSON (not YAML) as a string - The `prompt` field is optional and stores the user's natural language description for reference - When refining an existing custom chart, apply targeted edits to the existing SQL and spec rather than regenerating from scratch ## Field Configuration ### Data Types - **`nominal`**: Categorical data (strings, categories). Use for dimensions. - **`temporal`**: Time-based data (dates, timestamps). Use for time dimensions. - **`quantitative`**: Numerical data (counts, amounts). Use for measures. - **`value`**: Special type for multiple measures. Use only in color field with `rill_measures`. ### Axis Properties ```yaml x: field: category_name # Field name from metrics view type: nominal # Data type limit: 10 # Max values to display sort: -y # Sort order (see below) showNull: true # Include null values labelAngle: -45 # Label rotation angle; omit for automatic orientation (0, -45 or -90 based on available width) ``` ### Sort Options - `"x"` or `"-x"`: Sort by x-axis values (ascending/descending) - `"y"` or `"-y"`: Sort by y-axis values (ascending/descending) - `"y_delta"` or `"-y_delta"`: Sort by the change versus the comparison period (`"x_delta"` / `"-x_delta"` on the y field of a horizontal bar chart) - `"color"` or `"-color"`: Sort by color field (heatmaps) - `"measure"` or `"-measure"`: Sort by measure (donut charts) - Array of values: Custom sort order (e.g., `["Mon", "Tue", "Wed"]`) ### Y-Axis Properties ```yaml y: field: total_revenue type: quantitative zeroBasedOrigin: true # Start y-axis at zero ``` ### Horizontal Bars `bar_chart`, `stacked_bar` and `stacked_bar_normalized` draw horizontal bars when the measure is on `x` and the dimension on `y`: ```yaml x: field: total_revenue type: quantitative y: field: category_name type: nominal sort: -x # Sort categories by the measure (x-axis) ``` **Multiple measures:** ```yaml y: field: revenue fields: - revenue - cost - profit type: quantitative ``` ### Color Configuration **Simple color string:** ```yaml color: primary # Named color color: secondary color: "#FF5733" # Hex color color: hsl(210, 70%, 50%) # HSL color ``` **Field-based color:** ```yaml color: field: region type: nominal limit: 10 legendOrientation: top # top, bottom, left, right, none ``` **Custom color mapping:** ```yaml color: field: status type: nominal colorMapping: - value: "success" color: hsl(120, 70%, 45%) - value: "warning" color: hsl(45, 90%, 50%) - value: "error" color: hsl(0, 70%, 50%) ``` **Color scheme:** ```yaml color: field: score type: quantitative colorRange: mode: scheme scheme: sequential ``` ### Special Field: rill_measures Use `rill_measures` in the color field when displaying multiple measures in stacked charts: ```yaml color: field: rill_measures type: value legendOrientation: top y: field: revenue fields: - revenue - cost - profit type: quantitative ``` ## Advanced Features ### Dimension Filters Filter component data without affecting other components: ```yaml kpi_grid: metrics_view: sales_metrics measures: - total_revenue dimension_filters: region IN ('North America') AND status IN ('active') ``` ### Time Range Override Override the default time range for a specific component: ```yaml heatmap: metrics_view: activity_metrics time_range: preset: last_7_days # ... other config ``` ### Time Filters Override time settings with detailed control: ```yaml stacked_bar: metrics_view: sales_metrics time_filters: tr=P12M&compare_tr=rill-PY&grain=week # ... other config ``` ### Comparison Per Widget Components inherit the canvas time range and time comparison. `time_filters` overrides either one per component, using the same `tr` and `compare_tr` parameters as explore URLs. `inherit` is a special value for both: `tr=inherit` keeps the canvas time range, and once `tr` is set a missing `compare_tr` means no comparison. A comparison range is `rill-PP`, `rill-PD`, `rill-PW`, `rill-PM`, `rill-PQ`, `rill-PY`, or a custom `,` pair, and it applies even when the canvas comparison is off. Applies to `kpi_grid`, `table`, `pivot`, `leaderboard`, and time-series charts: | `time_filters` | Time range | Comparison | | --- | --- | --- | | (absent) | canvas | canvas | | `tr=inherit` | canvas | off | | `tr=inherit&compare_tr=rill-PP` | canvas | previous period | | `tr=P7D` | last 7 days | off | | `tr=P7D&compare_tr=inherit` | last 7 days | canvas | | `tr=P7D&compare_tr=rill-PP` | last 7 days | previous period | ```yaml kpi_grid: metrics_view: sales_metrics measures: - total_revenue time_filters: tr=inherit ``` ### Hiding Local Filters Components with `dimension_filters` or `time_filters` show the local filters as chips in the component header. Set `hide_local_filters: true` to hide the chips while the filters still apply to the component's data. Useful when the title already describes the filter: ```yaml kpi_grid: metrics_view: sales_metrics title: North America revenue measures: - total_revenue dimension_filters: region IN ('North America') hide_local_filters: true ``` ### Vega-Lite Configuration Customize chart appearance with Vega-Lite config: ```yaml bar_chart: metrics_view: sales_metrics vl_config: | { "axisX": { "grid": true, "labelAngle": 45 }, "range": { "category": ["#D63946", "#457B9D", "#F4A261", "#2A9D8F"] } } # ... other config ``` ## Complete Example ```yaml type: canvas display_name: "Monthly Business Report" defaults: time_range: P30D comparison_mode: time max_width: 1400 theme: corporate_theme rows: - height: 100px items: - width: 12 markdown: content: | # Monthly Business Report Comprehensive overview of business performance metrics. --- alignment: horizontal: center vertical: middle - height: 50px items: - width: 12 markdown: content: "## Key Metrics" alignment: horizontal: left vertical: middle - height: 200px items: - width: 12 kpi_grid: metrics_view: business_metrics measures: - revenue - profit - customers - orders comparison: - percent_change - previous sparkline: right - height: 50px items: - width: 12 markdown: content: "## Revenue Analysis" alignment: horizontal: left vertical: middle - height: 400px items: - width: 8 combo_chart: metrics_view: business_metrics title: "Revenue and Profit Margin" color: field: measures type: value legendOrientation: top x: field: report_date type: temporal limit: 30 y1: field: revenue type: quantitative mark: bar y2: field: profit_margin type: quantitative mark: line - width: 4 donut_chart: metrics_view: business_metrics title: "Revenue by Segment" innerRadius: 50 color: field: customer_segment type: nominal limit: 5 measure: field: revenue type: quantitative showTotal: true - height: 50px items: - width: 12 markdown: content: "## Regional Performance" alignment: horizontal: left vertical: middle - height: 350px items: - width: 6 leaderboard: metrics_view: business_metrics dimensions: - region measures: - revenue - profit - order_count num_rows: 8 - width: 6 heatmap: metrics_view: business_metrics title: "Revenue by Region and Product" color: field: revenue type: quantitative x: field: product_category type: nominal limit: 8 y: field: region type: nominal limit: 6 ```