Skip to main content

How to Build a Power BI Theme JSON from Scratch

April 13, 2026

By Tony Thomas

TL;DR: A Power BI theme JSON file is not complicated. It is just poorly documented for people who want to build one incrementally. This tutorial walks through five layers, each independently useful, from the smallest valid theme to a production-ready JSON you can version-control and share across your team.


Layer 1: The Minimum Viable Theme

A Power BI theme JSON file needs exactly two things to be valid: a name and at least one property override. Here is the smallest useful theme you can build:

{
  "name": "Company Theme v1",
  "dataColors": [
    "#2563EB",
    "#7C3AED",
    "#DB2777",
    "#D97706",
    "#059669",
    "#DC2626",
    "#0891B2",
    "#4F46E5"
  ]
}

Save this as a .json file. In Power BI Desktop, go to View > Themes > Browse for themes and select it. Every chart in your report now uses these eight colors instead of Power BI's defaults.

That is it. One property, and every bar chart, line chart, pie chart, and scatter plot in the report is using your palette.

The dataColors array is ordered. The first color is used for the first series in any chart, the second for the second series, and so on. Power BI cycles through the list when a chart has more series than colors. This means the order matters — put your most important or most brand-aligned color first.

Eight colors is the right number. Fewer than six and you will see repeats in common multi-series charts. More than ten and you are unlikely to have enough perceptual distance between adjacent colors for users to distinguish them reliably. Eight is the sweet spot where every color is distinct and you rarely cycle.

Choosing your eight colors

If you have a brand guide with 2-3 primary colors, you need to extend that palette to eight without making it look like a random collection. The approach that produces the most coherent results:

  1. Start with your primary brand color as color #1
  2. If you have a secondary brand color, use it as color #2
  3. Generate the remaining colors at evenly-spaced hue angles in OKLCH color space, keeping lightness and chroma consistent with your primary

OKLCH matters here because it is perceptually uniform — colors at the same lightness value actually look equally bright to the human eye, unlike HSL where a yellow at 50% lightness looks dramatically brighter than a blue at 50% lightness. If you generate your palette in HSL, the bar chart will look unbalanced even though the numbers say otherwise.

Tools like OKLCH Color Picker let you experiment with this directly. Set your primary color's lightness and chroma, then rotate the hue in 45-degree increments to generate candidates.


Layer 2: Background and Foreground

{
  "name": "Company Theme v1",
  "dataColors": ["#2563EB", "#7C3AED", "#DB2777", "#D97706", "#059669", "#DC2626", "#0891B2", "#4F46E5"],
  "background": "#FFFFFF",
  "foreground": "#1E293B",
  "tableAccent": "#F1F5F9"
}

Three new properties:

  • background sets the default page background color. White is the safe default. Some organizations use a light grey (#F8FAFC or similar) to give the page a slight depth behind white visual containers.

  • foreground sets the default text color for most text elements: axis labels, data labels, legend text, titles. Do not use pure black (#000000) — it creates harsh contrast on white that causes eye fatigue. A dark slate (#1E293B) or dark grey (#334155) reads cleanly without the harshness.

  • tableAccent controls the alternating row shading in table and matrix visuals. This is the property most theme builders forget. Left at the default, it applies a blue tint that clashes with carefully chosen brand colors. Set it to a neutral light grey that complements your background.

At this point you have a theme that controls the three most visible color decisions in any report: what color the data is, what color the text is, and what color the page is. For simple reports, this might be all you need.

The contrast check

Before moving on, verify these combinations pass WCAG contrast requirements:

  • foreground against background: needs 4.5:1 minimum for normal text
  • Each dataColor against background: needs 3:1 minimum for non-text elements (chart bars, lines)
  • foreground against tableAccent: needs 4.5:1 for text in shaded table rows

If any combination fails, adjust now. It is dramatically easier to fix contrast at the theme level than to discover it in a published report when someone files an accessibility complaint.


Layer 3: Text Classes

Power BI themes support textClasses — named text styles that control font family, size, weight, and color for different roles in the report:

{
  "name": "Company Theme v1",
  "dataColors": ["#2563EB", "#7C3AED", "#DB2777", "#D97706", "#059669", "#DC2626", "#0891B2", "#4F46E5"],
  "background": "#FFFFFF",
  "foreground": "#1E293B",
  "tableAccent": "#F1F5F9",
  "textClasses": {
    "callout": {
      "fontSize": 28,
      "fontFace": "Segoe UI",
      "color": "#1E293B"
    },
    "title": {
      "fontSize": 12,
      "fontFace": "Segoe UI",
      "fontWeight": 600,
      "color": "#1E293B"
    },
    "header": {
      "fontSize": 14,
      "fontFace": "Segoe UI Semibold",
      "color": "#1E293B"
    },
    "label": {
      "fontSize": 10,
      "fontFace": "Segoe UI",
      "color": "#475569"
    }
  }
}

The four text classes Power BI recognizes:

  • callout — the large number in KPI cards and card visuals. This is your display-level text.
  • title — visual titles. Controls the text above each chart, table, or card.
  • header — column headers in tables and matrices.
  • label — axis labels, data labels, legend items, and other secondary text.

Why Segoe UI? It is the default font in the Power BI service and renders consistently across Windows, web, and embedded scenarios. Custom fonts work in Power BI Desktop but can fail silently in the service or in embedded configurations, particularly older ones. Unless your brand guide specifically requires a different font and you have verified it renders correctly in your deployment environment, Segoe UI is the pragmatic choice.

Font sizes are in points, not pixels. A 12pt title renders at roughly 16px on screen. The sizes above produce a clear hierarchy: callout (28pt) is unambiguous as the primary number, headers (14pt) are visually distinct from labels (10pt), and titles (12pt) sit between them.


Layer 4: Visual-Specific Overrides

This is where theme JSON gets powerful and where most tutorials stop short. The visualStyles object lets you set default properties for specific visual types. Rather than formatting every bar chart individually, you define the default once in the theme.

{
  "visualStyles": {
    "*": {
      "*": {
        "general": [{ "responsive": true, "keepLayerOrder": true }],
        "border": [{ "show": false }],
        "visualHeader": [{ "show": false }],
        "background": [{
          "show": true,
          "color": { "solid": { "color": "#FFFFFF" } },
          "transparency": 0
        }]
      }
    },
    "barChart": {
      "*": {
        "dataPoint": [{ "showAllDataPoints": true }],
        "categoryAxis": [{
          "labelColor": { "solid": { "color": "#475569" } },
          "fontSize": 10
        }],
        "valueAxis": [{
          "labelColor": { "solid": { "color": "#475569" } },
          "fontSize": 10,
          "gridlineShow": true,
          "gridlineColor": { "solid": { "color": "#E2E8F0" } },
          "gridlineThickness": 1
        }]
      }
    },
    "card": {
      "*": {
        "labels": [{
          "fontSize": 28,
          "color": { "solid": { "color": "#1E293B" } }
        }],
        "categoryLabels": [{
          "fontSize": 10,
          "color": { "solid": { "color": "#64748B" } }
        }]
      }
    }
  }
}

The structure is visualStyles > [visual type or *] > [property group] > [property name]. The * wildcard applies to all visual types — use it for universal defaults like hiding borders and visual headers, then override specific visuals where needed.

Key decisions in the example above:

  • Borders off by default. Visual borders add visual noise without communicating information. If you want to separate visuals, use white space (gutters between visuals) rather than drawn borders.
  • Visual headers hidden. The three-dot menu and pin icon that appear when hovering over a visual are useful during development but distracting in production. Hide them globally and re-enable selectively if needed.
  • White visual backgrounds. On a white or light-grey page background, white visual containers with no border create a clean card-style layout when combined with consistent gutters.
  • Gridlines present but subtle. Light grey gridlines at 1px help users read values from bar charts without adding visual weight.
  • Consistent axis text. Setting axis label color and size in the theme means every chart uses the same treatment without manual formatting.

You can extend visualStyles to cover every visual type Power BI supports: lineChart, clusteredBarChart, pieChart, tableEx, pivotTable, and dozens more. The property names match what you see in the Format pane. Discovering them is the tedious part — Microsoft's documentation lists supported properties, and opening a visual's Format pane in Desktop while comparing against the JSON schema is the most reliable way to explore what is available.


Layer 5: Conditional Formatting Colors

If your reports use conditional formatting (and most analytical reports should, at least for KPIs), define those colors in the theme rather than hard-coding them in each visual:

{
  "bad": "#DC2626",
  "neutral": "#D97706",
  "good": "#059669",
  "maximum": "#2563EB",
  "center": "#F8FAFC",
  "minimum": "#DC2626"
}

These six properties set the default colors for conditional formatting rules across the report. When a user applies conditional formatting to a table column or card, these are the colors that populate the "minimum," "center," and "maximum" fields by default.

Why this matters: Without these defined, every developer on the team picks their own red/yellow/green, and the report ends up with three different shades of "bad" across different pages. Define them once in the theme and every conditional format is consistent.


Putting It All Together

Here is a complete, production-ready theme JSON combining all five layers:

{
  "name": "Company Theme v1",
  "dataColors": [
    "#2563EB", "#7C3AED", "#DB2777", "#D97706",
    "#059669", "#DC2626", "#0891B2", "#4F46E5"
  ],
  "background": "#FFFFFF",
  "foreground": "#1E293B",
  "tableAccent": "#F1F5F9",
  "bad": "#DC2626",
  "neutral": "#D97706",
  "good": "#059669",
  "maximum": "#2563EB",
  "center": "#F8FAFC",
  "minimum": "#DC2626",
  "textClasses": {
    "callout": { "fontSize": 28, "fontFace": "Segoe UI", "color": "#1E293B" },
    "title": { "fontSize": 12, "fontFace": "Segoe UI", "fontWeight": 600, "color": "#1E293B" },
    "header": { "fontSize": 14, "fontFace": "Segoe UI Semibold", "color": "#1E293B" },
    "label": { "fontSize": 10, "fontFace": "Segoe UI", "color": "#475569" }
  },
  "visualStyles": {
    "*": {
      "*": {
        "general": [{ "responsive": true, "keepLayerOrder": true }],
        "border": [{ "show": false }],
        "visualHeader": [{ "show": false }],
        "background": [{
          "show": true,
          "color": { "solid": { "color": "#FFFFFF" } },
          "transparency": 0
        }]
      }
    }
  }
}

Save this file in version control alongside the reports that use it. When the brand colors change, you update one file and every report that imports it inherits the change. That is the real value of a JSON theme over UI-based formatting: it is a single source of truth that scales across reports and across the team.


Hand-Editing vs. Using a Tool

Everything in this tutorial can be done in a text editor. The JSON structure is straightforward, and once you understand the five layers, building a theme takes 30-60 minutes of focused work plus testing.

The tedious parts are:

  1. Generating a perceptually balanced 8-color palette from a single brand color. This requires understanding OKLCH color space or using a tool that does.
  2. Checking contrast ratios for every color combination. A theme with 8 data colors, a background, a foreground, and a table accent produces 20+ combinations to verify.
  3. Discovering visualStyles property names. The Microsoft documentation is comprehensive but not organized for discovery. Finding the right property name for "axis gridline color in a clustered bar chart" requires digging.

If you want to shortcut the palette generation and contrast checking, Draft BI's Theme Studio takes your primary brand color, generates a complete OKLCH-derived palette, verifies every contrast ratio against WCAG requirements, and exports the theme JSON. You still get the JSON file — you just skip the manual color math.

For visualStyles exploration, opening the Format pane alongside the Microsoft theme documentation is the most reliable way to discover and map property names.


Maintenance and Versioning

A theme is not a one-time artifact. It evolves with the brand, with new visual types you adopt, and with accessibility requirements that tighten over time.

Version your theme file. Include a version in the name ("Company Theme v1.2") and store it in Git alongside your PBIR report files. When someone asks "why did the charts change color?" you can trace it to a specific commit.

Test with a reference report. Maintain a single Power BI report that includes every visual type your organization uses: bar chart, line chart, card, table, matrix, KPI, slicer. Apply every theme update to this report first. If the theme breaks table headers or makes pie chart labels unreadable, you catch it here before it reaches production.

Document your decisions. In a comment block at the top of the file (JSON does not support comments, but you can add a "_comment" property), note why specific colors were chosen: which brand guide version they reference, which WCAG ratios were verified, and when the last contrast audit was done.

{
  "_meta": {
    "brand_guide_version": "2026-Q1",
    "wcag_audit_date": "2026-04-15",
    "contrast_verified": true,
    "maintainer": "design-team@company.com"
  }
}

Further Reading


Ready to generate a complete, contrast-verified theme from your brand colors? Try Theme Studio free — paste your primary color and get a production-ready theme JSON in seconds. Works with any Power BI license.

Draft BI
Tony Thomas

Founder of Draft BI, building the design-first companion for Power BI report development. Writing about PBIR, WCAG accessibility, DAX measures, and the workflows that help Power BI developers and analysts deliver better reports faster.