Dataface YAML Style Guide¶
A comprehensive guide for writing clean, maintainable, and consistent Dataface board YAML files.
Table of Contents¶
- Naming Conventions
- Structure & Organization
- Type-Based Field Encapsulation
- Referencing & Modularity
- Templating & Expressions
- Formatting & Syntax
- Best Practices
Naming Conventions¶
Auto-Interpreted Titles from IDs/Slugs¶
Principle: Titles and names should be automatically interpreted from IDs/slugs when not explicitly provided.
Rule: If an object has an id or name field (or is a file-based board) but no title, the title should be auto-generated by converting the identifier to a human-readable format.
- For file-based boards, the filename (without extension) is the identifier.
- For nested boards, the explicit
idorname(if provided) is the identifier.
Examples:
# ✅ Good: Title auto-generated from filename 'sales_overview.yml' # File: sales_overview.yml # Implicit Title: "Sales Overview" rows: - title: "Q1 Details" # ... # ✅ Good: Explicit title overrides auto-generation # File: sales_overview.yml title: "Sales Dashboard - Q4 2024"
Auto-Generation Rules:
- Convert snake_case to Title Case: revenue_by_month → Revenue By Month
- Convert camelCase to Title Case: revenueByMonth → Revenue By Month
- Preserve acronyms: kpi_revenue → KPI Revenue
Identifier Naming¶
Board File Names:
# ✅ Good: lowercase with underscores sales_overview.yml monthly_sales_board.yml # ⚠️ Discouraged (but supported): spaces, special characters, camelCase Sales Overview.yml monthlySalesBoard.yml sales-board.yml # These will still be correctly converted to titles (e.g. "Sales Overview", "Monthly Sales Board") # but standardizing on snake_case is recommended for consistency.
Query Names:
# ✅ Preferred: Descriptive names without prefixes queries: sales: ... sales_by_region: ... product_categories: ... # ✅ Preferred: Use explicit namespacing when referencing queries query: queries.sales # Explicit - clearly a query query: queries.totals # Explicit - unambiguous # ✅ Also acceptable: Simple reference when context is clear query: sales # Works when unambiguous, but explicit is preferred
Chart IDs:
# ✅ Good: descriptive, indicates purpose charts: revenue_by_month: ... sales_trend: ... kpi_total_revenue: ... # ❌ Bad: generic, unclear purpose charts: chart1: ... chart: ... c1: ...
Variable Names:
# ✅ Good: clear, descriptive variables: region: ... date_range: ... min_revenue: ... # ❌ Bad: abbreviations, unclear variables: reg: ... dr: ... min_rev: ...
Structure & Organization¶
Top-Level Organization¶
Recommended Order:
1. Board metadata (title, description - name is file-based)
2. Variables (user inputs/filters)
3. Queries (data definitions)
4. Charts (reusable chart definitions - optional)
5. Boards (nested boards/sections)
Example:
# File: sales_dashboard.yml title: "Sales Dashboard" description: "Monthly sales metrics and trends" variables: # User inputs region: ... date_range: ... queries: # Data queries sales: ... regional: ... charts: # Optional: reusable chart definitions revenue_chart: ... rows: # Nested boards (previously sections) - title: "Overview" cols: ...
Grouping Related Elements¶
Group by Domain:
queries: # Sales queries sales: ... sales_by_region: ... sales_trends: ... # Product queries products: ... product_categories: ... # Customer queries customers: ... customer_segments: ...
Use Comments for Organization:
rows: # Overview board - high-level KPIs - title: "Overview" cols: ... # Trends board - time series analysis - title: "Trends" rows: ... # Details board - detailed tables - title: "Details" rows: ...
Type-Based Field Encapsulation¶
Principle: When an object has different fields per "type", use the type as the encapsulating key for those fields.
Rule: Type-specific fields should be nested under a key matching the type name. The presence of a type-specific key is sufficient to determine the type - no explicit layout:, type:, input:, or data_type: fields are needed.
Board Layout Types¶
Row Layout (presence of rows: key determines layout - stacks vertically):
rows: - title: "Row 1" cols: ... - title: "Row 2" cols: ...
Column Layout (presence of cols: key determines layout - stacks horizontally):
cols: - title: "Sidebar" width: "300px" rows: ... - title: "Main Content" rows: ...
Grid Layout (presence of grid: key determines layout):
grid: columns: 12 # 12-column grid row_height: 100px gap: md # Spacing: sm, md, lg, xl, or none default_width: 6 # Fallback width if no smart default exists default_height: 4 # Fallback height items: - kpi_revenue # Smart Sizing: Automatically 2x2 (KPI type) - chart1 # Flowing item: Uses defaults (6x4), placed automatically - width: 12 # Overrides width (full row) height: 6 # Overrides height item: chart2 - x: 0 # Pinned item: Explicit position y: 10 # Starts at row 10 width: 4 height: 4 item: chart3 - x: 2 # Overlapping item: Can share space with others y: 10 width: 2 item: chart4_overlay
Tab Layout (presence of tabs: key determines layout):
tabs: default: "overview" position: top boards: - title: "Overview" # Tab 1 (Nested Board) rows: ... - title: "Details" # Tab 2 (Nested Board) cols: ...
Default Layout: If no layout-specific key is present, row layout is used by default.
Query Types¶
Semantic Layer Query (presence of metrics: and/or dimensions: keys determines type):
queries: sales: metrics: [total_revenue] dimensions: [month]
dbt Model Query (presence of model: and columns: keys determines type):
queries: products: model: "ref('fct_products')" columns: [product_id, name, price]
Raw SQL Query (presence of sql: key determines type):
queries: custom: sql: | SELECT * FROM orders WHERE created_at >= {{ date_range.start }} LIMIT 100 profile: my_postgres # Required: dbt profile name
Type Inference Rules (checked in order):
1. If metrics: and/or dimensions: are present → Semantic Layer Query
2. If model: and columns: are present → dbt Model Query
3. If sql: is present → Raw SQL Query
4. If query value is a string (no other keys) → Raw SQL Query (default)
Note: SQL is the default query type, making it the most concise option for raw SQL queries. This covers the majority of use cases where you just need to write SQL directly.
Variable Input Types¶
Select Input (presence of select: key determines input type):
variables: region: select: options: static: ["North", "South", "East", "West"] placeholder: "Choose a region" default: "North"
Slider Input (presence of slider: key determines input type):
variables: min_revenue: slider: min: 0 max: 1000000 step: 1000 default: 10000
Date Range Input (presence of daterange: key determines input type):
variables: date_range: daterange: format: "YYYY-MM-DD" presets: ["last_7_days", "last_30_days", "last_quarter"] default: "2024-01-01"
Type Inference Rules:
- The presence of type-specific keys (select:, slider:, daterange:, datepicker:, checkbox:, radio:, input:, textarea:, multiselect:) determines the input type
- Data type is inferred from the input type (e.g., slider: → number, daterange: → date)
- No need for explicit input: or data_type: fields
Benefits of Type Encapsulation: - Clear separation of type-specific fields - Prevents field name conflicts - Makes schema validation easier - Improves IDE autocomplete support - Self-documenting structure - Reduces verbosity - no need for explicit type declarations when structure implies type - More intuitive - structure itself communicates intent
Key Principle: The structure itself is the type declaration. If you see grid: in a section, it's a grid layout. If you see sql: in a query, it's a SQL query. If you see daterange: in a variable, it's a date range input.
Referencing & Modularity¶
Principle: Allow referencing from higher-level structures or external files for flexible organization, while also permitting definitions at lower levels for convenience.
Hierarchical References¶
Charts at Board Level:
# File: sales_overview.yml charts: revenue_chart: title: "Revenue" query: sales type: bar rows: - revenue_chart
Charts at Nested Board Level:
# File: sales_overview.yml rows: - charts: # Define charts locally in this nested board revenue_chart: title: "Revenue" query: sales type: bar orders_chart: title: "Orders" query: sales type: line cols: # Reference the locally defined charts - revenue_chart - orders_chart
Queries at Board Level:
# File: sales_overview.yml queries: sales: metrics: [total_revenue] rows: - title: "Revenue" # Reference query from board level query: sales type: bar
Queries at Chart Level:
# File: sales_overview.yml rows: - title: "Revenue" # Define query inline at chart level query: metrics: [total_revenue] dimensions: [month] type: bar
External File References¶
Principle: Dataface operates on a Project Context. All YAML files in your project are automatically loaded and available as namespaces. You don't need explicit import statements; the filename itself is the namespace.
Syntax: filename.resource_id
* filename: The name of the file without extension (e.g., _shared for _shared.yml)
* resource_id: The key of the resource within that file (e.g., sales)
Note on Uniqueness: Resource IDs (queries, charts, variables) must be unique within a file and across all imported files. Each type (queries, charts, variables) has its own namespace, so you can have a query and chart with the same name. However, duplicate names within the same type (e.g., two queries both named sales) are not allowed, even if they're in different files that are imported together. If you have a naming collision, use explicit namespacing: queries.sales vs charts.sales.
Explicit Referencing (Optional):
You can also use the explicit path filename.type.id if you prefer clarity or need to resolve ambiguity.
- _sales_queries.queries.sales
- _sales_charts.charts.revenue_trend
Referencing External Queries:
# File: _sales_queries.yml queries: sales: ... # File: sales_overview.yml rows: - title: "Revenue" query: _sales_queries.sales # 'sales' from '_sales_queries.yml' type: bar
Note on Implicit Namespacing:
This "Project Context" approach treats filenames as global variables. While convenient, it can feel "magical" compared to explicit imports (like dbt's ref() or Python's import).
- Benefit: Reduces boilerplate; valid YAML structure (no jinja brackets needed).
- Risk: Potential naming collisions between local variables and filenames.
- Best Practice: Use unique, descriptive filenames (especially for partials using _) to avoid conflicts with local keys.
Referencing External Charts:
# File: _shared_charts.yml charts: revenue_trend: type: line ... # File: sales_overview.yml rows: - _shared_charts.revenue_trend # Reference namespaced chart
Referencing External Variables:
# File: _common_vars.yml variables: region: ... # File: sales_overview.yml queries: sales: filters: region: _common_vars.region
Reusing Nested Boards: You can reference a specific section (nested board) of another file if you give it an ID.
# File: sales_dashboard.yml rows: - id: kpi_section # Give the section an ID title: "KPIs" cols: ... # File: executive_summary.yml rows: - sales_dashboard.kpi_section # Reference the section by ID
When to Use Each Approach¶
Use Top-Level Definitions When: - Charts/queries are reused across multiple nested boards - You want centralized organization - Building complex boards with many components
Use Nested Definitions When: - Charts are specific to one nested board - You want to keep related code together - Building simple boards
Use External Partial References When: - Sharing components across multiple board files - Organizing large boards into modules - Building a library of reusable components
Example: Hybrid Approach:
# File: sales_overview.yml # Shared queries used across nested boards queries: sales: ... products: ... # Shared charts used in multiple nested boards charts: revenue_chart: ... rows: - title: "Overview" cols: # Mix of references and inline definitions - revenue_chart # Reference from top level - orders_chart: # Inline definition query: sales type: bar - title: "Details" rows: # Inline query definition for specific chart - detail_chart: query: metrics: [total_revenue] dimensions: [day, product] type: table
Templating & Expressions¶
Principle: Use Jinja2 templating for dynamic values, logic, and variable substitution. Templating is allowed in SQL strings, text content, labels, and filter values.
Basic Syntax¶
- Output:
{{ variable }}- Renders the value of a variable or expression. - Logic: `` - Control flow (if/else, loops).
- Comments: `` - Comments that won't be rendered.
Referencing Variables¶
# ✅ Good: Direct variable reference in SQL queries: sales: sql: | SELECT * FROM orders WHERE region = '{{ region }}' profile: my_postgres # ✅ Good: Using variables in markdown content rows: - title: "Overview for {{ region }}" content: | Showing sales data for **{{ region }}** from {{ date_range.start }} to {{ date_range.end }}.
Logic and Control Flow¶
Use logic to make queries or content dynamic based on inputs.
# ✅ Good: Conditional SQL generation queries: dynamic: sql: | SELECT * FROM sales {% if region != 'All' %} WHERE region = '{{ region }}' {% endif %} profile: my_postgres
Best Practices¶
- Keep Logic Simple: Avoid complex business logic in templates. Move complex logic to dbt models or database views if possible.
- Quote Strings: Remember to quote string variables in SQL:
'{{ region }}'. - Use Filters: Use Jinja filters for formatting:
{{ revenue | number_format }}.
Formatting & Syntax¶
Indentation¶
Use 2 spaces (not tabs) for indentation:
# ✅ Good: 2 spaces title: "Sales Dashboard" queries: sales: ... # ❌ Bad: tabs or 4 spaces title: "Sales Dashboard" queries: ...
Lists vs Maps¶
Use Lists for Ordered Collections:
# ✅ Good: list for ordered sections rows: - title: "First Section" cols: ... - title: "Second Section" cols: ...
Use Maps for Named Collections:
# ✅ Good: map for named queries queries: sales: ... products: ... # ✅ Good: map for named charts charts: revenue_chart: ... orders_chart: ...
String Formatting¶
Use Quotes When Needed:
# ✅ Good: quotes for strings with special characters title: "Sales Dashboard - Q4 2024" description: "Dashboard showing sales metrics" # ✅ Good: no quotes for simple strings name: sales_dashboard type: bar # ✅ Good: quotes for strings starting with numbers id: "2024_sales"
Multi-line Strings:
# ✅ Good: use | for literal block scalar content: | # Sales Performance This quarter showed strong growth. Key highlights: - Revenue up 15% - Orders increased 20% # ✅ Good: use > for folded block scalar (single line) description: > This dashboard shows monthly sales metrics and trends by region.
Arrays¶
Inline Arrays:
# ✅ Good: inline for short arrays metrics: [total_revenue, order_count] dimensions: [month, region] # ✅ Good: multi-line for longer arrays metrics: - total_revenue - order_count - avg_order_value - customer_count
Comments¶
Use Comments Liberally:
# File: sales_dashboard.yml title: "Sales Dashboard" # User input filters variables: region: ... # Data queries - grouped by domain queries: # Sales queries sales: ... # Product queries products: ... # Board layout rows: # Overview board - title: "Overview" charts: ...
Best Practices¶
1. Start Simple, Add Complexity Gradually¶
# ✅ Good: Start with minimal required fields # File: sales_dashboard.yml queries: sales: metrics: [total_revenue] dimensions: [month] charts: revenue_chart: query: sales type: bar rows: - revenue_chart
Then add features as needed: - Add titles when auto-generated ones aren't sufficient - Add variables when interactivity is needed - Add styling when default appearance needs customization
2. Keep Layouts Clean with References¶
Principle: Strictly separate Definition from Layout.
- Definitions: Define charts and queries in charts: and queries: sections.
- Layout: Use rows, cols, and grid only to arrange references or nested boards.
Avoid defining charts inline within layout arrays. This keeps the visual structure clean and readable.
# ✅ Good: Layout contains only references and structural boards charts: revenue_chart: ... orders_chart: ... rows: - cols: - revenue_chart - orders_chart - title: "Trends" # Nested Board rows: ... # ⚠️ Discouraged: Inline chart definitions clutter the layout rows: - type: bar # Inline chart query: sales
3. Use Explicit Tags for Clarity¶
Principle: If a complex inline structure feels ambiguous or confusing, use YAML Tags (!Chart, !Board, !Query) to make the type explicit.
rows: - !Board title: "Complex Nested Section" charts: local_chart: !Chart type: bar query: sales cols: - local_chart
4. Use Descriptive Names¶
# ✅ Good: Clear, descriptive names queries: sales_by_region_and_month: ... charts: revenue_trend_by_region: ... # ❌ Bad: Unclear abbreviations queries: sbr: ... charts: rtbr: ...
4. Group Related Elements¶
# ✅ Good: Grouped by domain queries: # Sales domain sales: ... sales_by_region: ... sales_trends: ... # Product domain products: ... product_categories: ... # ❌ Bad: Mixed organization queries: sales: ... products: ... sales_by_region: ... product_categories: ...
5. Use Type Encapsulation Consistently¶
# ✅ Good: Type-specific fields under type key rows: - grid: columns: 12 gap: md items: - width: 6 item: chart1 # ❌ Bad: Type-specific fields at wrong level rows: - grid: columns: 12 # Should be under 'grid' items: ... gap: md # Should be under 'grid'
6. Document Complex Logic¶
# ✅ Good: Comments explain complex logic queries: filtered_sales: metrics: [total_revenue] dimensions: [month] filters: # Use 'All' to show all regions, otherwise filter by selected region region: "{{ region }}" # Date range filter using variable order_date: "{{ date_range }}"
7. Validate Early and Often¶
# ✅ Good: Validate YAML syntax and structure # Use: df validate sales_dashboard.yml # ✅ Good: Test incrementally # Start with minimal board, add features one at a time
Summary¶
This style guide establishes conventions for writing clean, maintainable Dataface dashboard YAML files. Key takeaways:
- Auto-generate titles from IDs/slugs when not explicitly provided
- Use type encapsulation for type-specific fields (e.g.,
grid:,rows:,cols:,tabs:) - Keep layouts clean by using references instead of inline definitions
- Support flexible referencing - allow definitions at multiple levels and external files
- Follow consistent naming conventions (
snake_casefor identifiers) - Organize logically - group related elements, use comments
- Format consistently - 2-space indentation, clear structure
- Start simple - add complexity only when needed
By following these conventions, your Dataface dashboards will be: - Readable: Easy to understand at a glance - Maintainable: Easy to modify and extend - Reusable: Components can be shared across dashboards - Consistent: Predictable structure across projects
Validating Your YAML¶
After writing your dashboard YAML, validate it to catch syntax and structure errors:
For strict validation that fails on warnings:
See the CLI Reference for more validation options.
Related Documentation¶
- CLI Reference - Validate and compile commands
- Best Practices Guide - General dashboard development best practices
- Troubleshooting Guide - Common YAML syntax issues
- Field Reference - Complete field reference
- Quick Reference - Quick reference cheat sheet
- YAML Specification - Technical YAML specification