Skip to content
Back to skills

Source 49fd0df75f9134176f2cbadc

ASecurity

Detailed instructions and examples for developing canvas dashboard resources in Rill

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 7, 2026
datagosqlexpressgitsecurityperformancedocumentation

Works with

  • cli

Security analysis

A100/100

Pro scans all 3 files and shows the line behind each finding

Scanned November 7, 2026

npx -y skills add gutsofpratik/OpenClaude-Skills --skill source-49fd0df75f9134176f2cbadc --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Source 49fd0df75f9134176f2cbadc?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Source 49fd0df75f9134176f2cbadc
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/gutsofpratik-source-49fd0df75f9134176f2cbadc/badge)](https://www.skillsdirectory.com/skills/gutsofpratik-source-49fd0df75f9134176f2cbadc)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
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: <name>` 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('<grain>', <time_dimension>)` 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 `<start>,<end>` 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
```

Files in this skill

  • PROVENANCE.json197 B
  • SKILL.md39.7 KB
  • UPSTREAM-LICENSE.txt11.1 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.