or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

api

features

charts

charts.mdconditional-formatting.mdvisualizations.md
authorization.mdchangesets.mdcharts-as-code.mdcompiler.mddashboards.mddbt.mdee-features.mdformatting.mdparameters.mdpivot.mdprojects-spaces.mdsql-runner.mdtemplating.mdwarehouse.md
index.md
tile.json

conditional-formatting.mddocs/api/features/charts/

Conditional Formatting

Conditional Formatting provides a comprehensive system for applying visual formatting rules to charts and tables based on field values. It supports single color rules with filter operators, color gradient ranges, field comparisons, and automatic min/max calculation.

Capabilities

Conditional Formatting Configuration

Core configuration types for conditional formatting rules.

/**
 * Min and max value range
 */
interface ConditionalFormattingMinMax<T = number> {
  min: T;
  max: T;
}

/**
 * Color gradient range with start and end colors
 */
interface ConditionalFormattingColorRange {
  /** Starting color (hex format) */
  start: string;
  /** Ending color (hex format) */
  end: string;
}

/**
 * Conditional formatting rule with static values
 */
interface ConditionalFormattingWithValues<T = number | string>
  extends BaseFilterRule<FilterOperator, T> {
  /** Values to compare against */
  values: T[];
}

/**
 * Conditional formatting rule comparing to another field
 */
interface ConditionalFormattingWithCompareTarget<T = number | string>
  extends BaseFilterRule<FilterOperator, T> {
  /** Target field to compare against */
  compareTarget: FieldTarget | null;
  /** Optional values for additional comparison */
  values?: T[];
}

/**
 * Union type for filter operator-based rules
 */
type ConditionalFormattingWithFilterOperator<T = number | string> =
  | ConditionalFormattingWithValues<T>
  | ConditionalFormattingWithCompareTarget<T>;

/**
 * Type guard for value-based rules
 */
function isConditionalFormattingWithValues(
  rule: ConditionalFormattingWithFilterOperator
): rule is ConditionalFormattingWithValues;

/**
 * Type guard for field comparison rules
 */
function isConditionalFormattingWithCompareTarget(
  rule: ConditionalFormattingWithFilterOperator
): rule is ConditionalFormattingWithCompareTarget;

Single Color Formatting

Apply a single color based on filter operator rules.

/**
 * Single color configuration with multiple rule conditions
 */
interface ConditionalFormattingConfigWithSingleColor {
  /** Target field (null applies to all fields) */
  target: FieldTarget | null;
  /** Color to apply when rules match (hex format) */
  color: string;
  /** Array of rules - all must match */
  rules: ConditionalFormattingWithFilterOperator[];
}

/**
 * Type guard for single color config
 */
function isConditionalFormattingConfigWithSingleColor(
  rule: ConditionalFormattingConfig
): rule is ConditionalFormattingConfigWithSingleColor;

/**
 * Creates a new single color configuration
 * @param defaultColor - Default hex color
 * @param target - Optional field target
 * @returns Configuration with one rule containing EQUALS operator and empty values array
 */
function createConditionalFormattingConfigWithSingleColor(
  defaultColor: string,
  target?: FieldTarget | null
): ConditionalFormattingConfigWithSingleColor;

/**
 * Creates a new rule with static values
 * @returns Rule with EQUALS operator and empty values
 */
function createConditionalFormattingRuleWithValues(): ConditionalFormattingWithFilterOperator;

/**
 * Creates a new rule comparing to another field
 * @returns Rule with EQUALS operator and null target
 */
function createConditionalFormattingRuleWithCompareTarget(): ConditionalFormattingWithCompareTarget;

/**
 * Creates a new rule comparing to field with additional values
 * @returns Rule with EQUALS operator, null target, and empty values
 */
function createConditionalFormattingRuleWithCompareTargetValues(): ConditionalFormattingWithCompareTarget;

Usage Example:

import {
  createConditionalFormattingConfigWithSingleColor,
  createConditionalFormattingRuleWithValues,
  FilterOperator,
  type ConditionalFormattingConfigWithSingleColor,
} from '@lightdash/common';

// Create config with static value rules
const singleColorConfig: ConditionalFormattingConfigWithSingleColor = {
  target: { fieldId: 'customers.total_revenue' },
  color: '#FF0000',
  rules: [
    {
      id: 'rule_1',
      operator: FilterOperator.GREATER_THAN,
      values: [1000],
    },
    {
      id: 'rule_2',
      operator: FilterOperator.LESS_THAN,
      values: [10000],
    }
  ],
};

// Or use helper function
const config = createConditionalFormattingConfigWithSingleColor(
  '#FF0000',
  { fieldId: 'customers.total_revenue' }
);

// Add rule
config.rules.push({
  id: 'rule_1',
  operator: FilterOperator.GREATER_THAN,
  values: [1000],
});

Color Range Formatting

Apply a color gradient based on numeric ranges.

/**
 * Color gradient configuration with min/max range
 */
interface ConditionalFormattingConfigWithColorRange {
  /** Target field (null applies to all fields) */
  target: FieldTarget | null;
  /** Color gradient range */
  color: ConditionalFormattingColorRange;
  /** Min/max value range (use 'auto' for automatic calculation) */
  rule: ConditionalFormattingMinMax<number | 'auto'>;
}

/**
 * Type guard for color range config
 */
function isConditionalFormattingConfigWithColorRange(
  config: ConditionalFormattingConfig
): config is ConditionalFormattingConfigWithColorRange;

/**
 * Creates a new color range configuration
 * @param defaultColor - Default end color (hex)
 * @param target - Optional field target
 * @returns Configuration with white-to-color gradient
 */
function createConditionalFormattingConfigWithColorRange(
  defaultColor: string,
  target?: FieldTarget | null
): ConditionalFormattingConfigWithColorRange;

Usage Example:

import {
  createConditionalFormattingConfigWithColorRange,
  type ConditionalFormattingConfigWithColorRange,
} from '@lightdash/common';

// Manual min/max
const colorRangeConfig: ConditionalFormattingConfigWithColorRange = {
  target: { fieldId: 'orders.revenue' },
  color: {
    start: '#FFFFFF',
    end: '#0066FF',
  },
  rule: {
    min: 0,
    max: 100000,
  },
};

// Automatic min/max calculation from data
const autoConfig: ConditionalFormattingConfigWithColorRange = {
  target: { fieldId: 'orders.revenue' },
  color: {
    start: '#FFFFFF',
    end: '#0066FF',
  },
  rule: {
    min: 'auto',
    max: 'auto',
  },
};

// Or use helper function
const config = createConditionalFormattingConfigWithColorRange(
  '#0066FF',
  { fieldId: 'orders.revenue' }
);

Combined Configuration Type

Union type for all conditional formatting configurations.

/**
 * Union of all conditional formatting config types
 */
type ConditionalFormattingConfig =
  | ConditionalFormattingConfigWithSingleColor
  | ConditionalFormattingConfigWithColorRange;

/**
 * Configuration type identifier
 */
enum ConditionalFormattingConfigType {
  Single = 'single',
  Range = 'range',
}

/**
 * Get configuration type from config object
 * @param rule - Configuration to check
 * @returns Configuration type
 * @throws Error if invalid configuration
 */
function getConditionalFormattingConfigType(
  rule: ConditionalFormattingConfig
): ConditionalFormattingConfigType;

Utility Functions

Helper functions for working with conditional formatting.

/**
 * Map of field IDs to min/max values
 */
type ConditionalFormattingMinMaxMap = Record<
  string,
  ConditionalFormattingMinMax
>;

/**
 * Row field values with metadata
 * Note: ItemsMap type is defined in explore-fields.md
 */
type ConditionalFormattingRowFields = Record<
  string,
  {
    field: ItemsMap[string];
    value: unknown;
  }
>;

/**
 * Comparison type for conditional rules
 */
enum ConditionalFormattingComparisonType {
  VALUES = 'values',
  TARGET_FIELD = 'target_field',
  TARGET_TO_VALUES = 'target_to_values',
}

/**
 * Label representation of a conditional formatting rule
 */
type ConditionalRuleLabel = {
  /** Field ID */
  field: string;
  /** Operator as string */
  operator: string;
  /** Optional value representation */
  value?: string;
};

/**
 * Check if field has percentage format
 * @param field - Field to check
 * @returns True if field uses percentage format
 */
function hasPercentageFormat(
  field: ItemsMap[string] | undefined
): boolean;

/**
 * Convert value based on field format (e.g., multiply by 100 for percentages)
 * @param value - Value to convert
 * @param field - Field with format information
 * @returns Converted value
 */
function convertFormattedValue<T extends unknown>(
  value: T,
  field: ItemsMap[string] | undefined
): T | number;

/**
 * Get overall min/max from min/max map
 * @param minMaxMap - Map of field min/max values
 * @returns Combined min and max across all fields
 */
function getMinMaxFromMinMaxMap(
  minMaxMap: ConditionalFormattingMinMaxMap
): ConditionalFormattingMinMax;

/**
 * Check if value matches conditional formatting rules
 * @param field - Field being evaluated
 * @param value - Value to check
 * @param minMaxMap - Min/max values for range calculations
 * @param config - Conditional formatting configuration
 * @param rowFields - Other field values in the row for comparisons
 * @returns True if rules match
 */
function hasMatchingConditionalRules(
  field: ItemsMap[string],
  value: unknown,
  minMaxMap: ConditionalFormattingMinMaxMap,
  config: ConditionalFormattingConfig | undefined,
  rowFields?: ConditionalFormattingRowFields
): boolean;

Usage Example:

import {
  hasPercentageFormat,
  convertFormattedValue,
  getMinMaxFromMinMaxMap,
  type ConditionalFormattingMinMaxMap,
} from '@lightdash/common';

// Check percentage format
const isPercent = hasPercentageFormat(field);

// Convert percentage values (0.25 -> 25)
const displayValue = convertFormattedValue(0.25, percentField);
// displayValue = 25

// Get overall min/max
const minMaxMap: ConditionalFormattingMinMaxMap = {
  'orders.revenue': { min: 0, max: 10000 },
  'orders.profit': { min: -500, max: 3000 },
};
const overallRange = getMinMaxFromMinMaxMap(minMaxMap);
// overallRange = { min: -500, max: 10000 }

Conditional Formatting Expression Evaluation

Evaluates conditional expressions in format strings with parameter substitution for dynamic formatting.

/**
 * Evaluates a conditional expression string with parameter substitution
 * Supports ${condition ? trueValue : falseValue} syntax with parameter references
 * Conditions can use == and != operators, or truthy checks
 *
 * @param formatString - String containing ${...} placeholders with conditional expressions
 * @param parameters - Record of parameter values for substitution
 * @returns Evaluated string with placeholders replaced by expression results
 *
 * @example
 * // Simple conditional
 * evaluateConditionalFormatExpression('${currency=="USD" ? "$" : "€"}', { currency: "USD" })
 * // Returns: "$"
 *
 * @example
 * // Parameter comparison
 * evaluateConditionalFormatExpression('${region=="NA" ? "North America" : "Other"}', { region: "EU" })
 * // Returns: "Other"
 *
 * @example
 * // Multiple parameters
 * evaluateConditionalFormatExpression('${currency} ${amount}', { currency: "USD", amount: 100 })
 * // Returns: "USD 100"
 */
function evaluateConditionalFormatExpression(
  formatString: string,
  parameters?: Record<string, unknown>
): string;

Usage Example:

import { evaluateConditionalFormatExpression } from '@lightdash/common';

// Conditional formatting based on parameter
const format = evaluateConditionalFormatExpression(
  '${currency=="USD" ? "$" : "€"}#,##0.00',
  { currency: 'USD' }
);
// Returns: "$#,##0.00"

// Multiple conditions
const label = evaluateConditionalFormatExpression(
  'Total: ${region=="NA" ? "North America" : "Europe"}',
  { region: 'NA' }
);
// Returns: "Total: North America"

// Truthy check
const prefix = evaluateConditionalFormatExpression(
  '${showPrefix ? "Value: " : ""}${amount}',
  { showPrefix: true, amount: 100 }
);
// Returns: "Value: 100"

Conditional Formatting Errors

Error handling for conditional formatting operations.

/**
 * Error thrown during conditional formatting operations
 */
class ConditionalFormattingError extends LightdashError {
  constructor(message: string);
}

Common Usage Patterns

Basic Single Color Rule

import {
  createConditionalFormattingConfigWithSingleColor,
  FilterOperator,
} from '@lightdash/common';

// Highlight values greater than threshold
const config = createConditionalFormattingConfigWithSingleColor(
  '#FF0000',
  { fieldId: 'customers.total_orders' }
);

config.rules = [
  {
    id: 'rule_1',
    operator: FilterOperator.GREATER_THAN,
    values: [100],
  }
];

Field Comparison

import {
  createConditionalFormattingConfigWithSingleColor,
  FilterOperator,
} from '@lightdash/common';

// Highlight when revenue exceeds target
const config = createConditionalFormattingConfigWithSingleColor(
  '#00FF00',
  { fieldId: 'orders.actual_revenue' }
);

config.rules = [
  {
    id: 'rule_1',
    operator: FilterOperator.GREATER_THAN,
    compareTarget: { fieldId: 'orders.target_revenue' },
  }
];

Heat Map with Color Gradient

import {
  createConditionalFormattingConfigWithColorRange,
} from '@lightdash/common';

// Create heat map with auto min/max
const config = createConditionalFormattingConfigWithColorRange(
  '#FF0000',
  null  // Apply to all numeric fields
);

config.rule = {
  min: 'auto',
  max: 'auto',
};

config.color = {
  start: '#FFFFFF',
  end: '#FF0000',
};

Multiple Conditions

import {
  createConditionalFormattingConfigWithSingleColor,
  FilterOperator,
} from '@lightdash/common';

// Highlight values in specific range
const config = createConditionalFormattingConfigWithSingleColor(
  '#FFAA00',
  { fieldId: 'metrics.conversion_rate' }
);

config.rules = [
  {
    id: 'rule_1',
    operator: FilterOperator.GREATER_THAN_OR_EQUAL,
    values: [0.1],
  },
  {
    id: 'rule_2',
    operator: FilterOperator.LESS_THAN,
    values: [0.5],
  }
];

Utility Functions

Utility functions for working with conditional formatting values and rules.

Value Conversion and Format Detection

/**
 * Checks if a field has percentage formatting
 * @param field - Field from items map
 * @returns True if field format is percentage
 */
function hasPercentageFormat(field: ItemsMap[string] | undefined): boolean;

/**
 * Converts formatted values for conditional formatting comparison
 * Multiplies percentage-formatted values by 100 for proper comparison
 * @param value - Value to convert
 * @param field - Field from items map
 * @returns Converted value (multiplied by 100 for percentages)
 */
function convertFormattedValue<T extends unknown>(
  value: T,
  field: ItemsMap[string] | undefined
): T | number;

Usage Example:

import { hasPercentageFormat, convertFormattedValue } from '@lightdash/common';

// Check if field has percentage format
const isPercent = hasPercentageFormat(field);

// Convert value for comparison
// If field is percentage and value is 0.25, returns 25
const convertedValue = convertFormattedValue(0.25, field);

Min/Max Aggregation

/**
 * Gets overall min and max from a min/max map across all fields
 * @param minMaxMap - Map of field IDs to their min/max values
 * @returns Object with overall min and max values
 */
function getMinMaxFromMinMaxMap(
  minMaxMap: ConditionalFormattingMinMaxMap
): { min: number; max: number };

/**
 * Map of field IDs to their min/max value ranges
 */
type ConditionalFormattingMinMaxMap = Record<
  string,
  ConditionalFormattingMinMax
>;

Usage Example:

import { getMinMaxFromMinMaxMap } from '@lightdash/common';

const minMaxMap = {
  'field_1': { min: 10, max: 100 },
  'field_2': { min: 5, max: 200 },
  'field_3': { min: 50, max: 150 }
};

// Returns { min: 5, max: 200 }
const overallMinMax = getMinMaxFromMinMaxMap(minMaxMap);

Error Handling

/**
 * Custom error class for conditional formatting validation and execution errors
 */
class ConditionalFormattingError extends LightdashError {
  constructor(message: string);
}

Usage Example:

import { ConditionalFormattingError } from '@lightdash/common';

try {
  // ... conditional formatting logic
  if (invalidRule) {
    throw new ConditionalFormattingError('Invalid rule configuration');
  }
} catch (error) {
  if (error instanceof ConditionalFormattingError) {
    console.error('Formatting error:', error.message);
  }
}