> ## Documentation Index
> Fetch the complete documentation index at: https://learn.nextedy.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Apply Conditional Formatting

> Apply dynamic visual styling to Nextedy RISKSHEET cells that changes automatically based on cell values, using `cellDecorators` for logic and `styles` for appearance in your sheet configuration.

export const LastReviewed = ({date}) => {
  if (!date) return null;
  const formatted = new Date(`${date}T00:00:00Z`).toLocaleDateString("en-US", {
    year: "numeric",
    month: "long",
    day: "numeric",
    timeZone: "UTC"
  });
  return <p className="mt-10 text-sm text-gray-400 dark:text-zinc-500 not-prose">
      Last reviewed on {formatted}
    </p>;
};

## Prerequisites

* Admin access to modify the sheet configuration (`canAdmin` must be `true`)
* Familiarity with the [configuration editor](/risksheet/guides/configuration/using-config-editor)
* Basic understanding of CSS properties (background-color, color, font-weight)

## How Conditional Formatting Works

Conditional formatting in Risksheet uses a two-part system:

1. **`cellDecorators`** --- JavaScript functions that evaluate cell data and toggle CSS classes on the cell element
2. **`styles`** --- CSS class definitions that control the visual appearance applied by decorators

The decorator function runs every time a cell is rendered. It receives the cell's value and the full row data, then adds or removes CSS classes based on conditions you define. The `styles` section injects those CSS classes into the page, controlling colors, fonts, and other visual properties.

<Frame>
  <img src="https://mintcdn.com/none-17b4493f/yWl5nA2D0IzEnZC1/risksheet/diagrams/guides/styling/conditional-formatting/diagram-1.svg?fit=max&auto=format&n=yWl5nA2D0IzEnZC1&q=85&s=5e384cd146a933418a819e159e5f7bb4" alt="diagram" style={{ maxWidth: "560px", width: "100%" }} width="560" height="220" data-path="risksheet/diagrams/guides/styling/conditional-formatting/diagram-1.svg" />
</Frame>

<Steps>
  <Step title="Define CSS Styles">
    Add named CSS class definitions to the `styles` section in your sheet configuration. Each entry maps a CSS class selector to a string of CSS properties. The class name must start with a dot (`.`), and the CSS properties **must be wrapped in curly braces**. Without the braces, styles may fail to apply.

    ### RPN Threshold Styles

    This example defines a three-tier color scheme for Risk Priority Number (RPN) values:

    ```yaml theme={null}
    styles:
      .boldCol: '{font-weight: 600;}'
      .rpn1: '{background-color: #eaf5e9 !important; color: #1d5f20 !important;}'
      .rpn2: '{background-color: #fff3d2 !important; color: #735602 !important;}'
      .rpn3: '{background-color: #f8eae7 !important; color: #ab1c00 !important;}'
    ```

    | CSS Class  | Visual Effect                     | Typical Use                |
    | ---------- | --------------------------------- | -------------------------- |
    | `.boldCol` | Bold text (font-weight: 600)      | Emphasize computed columns |
    | `.rpn1`    | Green background, dark green text | Low risk (RPN 0--150)      |
    | `.rpn2`    | Amber background, dark amber text | Medium risk (RPN 151--250) |
    | `.rpn3`    | Red background, dark red text     | High risk (RPN 251+)       |

    <Warning title="Always wrap style values in curly braces">
      Every entry under `styles` must wrap its CSS rules in `{}` braces, for example `'.rpn1': '{background-color: #eaf5e9 !important;}'`. Without the braces, the engine cannot parse the rule and your conditional formatting will not appear.
    </Warning>

    <Warning title="Always use `!important` in style definitions">
      Risksheet applies default grid styles that override custom styles without `!important`. Include `!important` on `background-color` and `color` properties in the `styles` section. Without it, your conditional formatting colors will not appear.
    </Warning>

    <Tip title="RPN thresholds are project-specific">
      The 150 / 250 thresholds shown above are typical defaults from a verified FMEA configuration, but Risksheet imposes no product-wide default. Tune the thresholds in your decorator function to match the risk acceptance criteria defined by your safety, quality, or cybersecurity process.
    </Tip>

    ### Header Group Styles

    You can also define styles for column group headers using compound selectors:

    ```yaml theme={null}
    styles:
      .firstRow .headSysReq: '{background-color: rgba(62,175,63,0.12) !important; color: #2A792D !important;}'
      .lastRow .headSysReq: '{background-color: #FFF !important; color: #2A792D !important;}'
      .firstRow .headFinalRanking: '{background-color: rgba(62,175,63,0.12) !important; color: #2A792D !important;}'
      .lastRow .headFinalRanking: '{background-color: #FFF !important; color: #2A792D !important;}'
    ```

    These compound selectors apply different colors to the first and last rows of header groups, creating a gradient effect in multi-row column headers.
  </Step>

  <Step title="Create Cell Decorator Functions">
    Add JavaScript functions to the `cellDecorators` section. Each decorator is keyed by the column it targets. The function receives an `info` object and uses jQuery's `toggleClass` method to conditionally apply CSS classes.

    ### RPN Column Decorator

    ```yaml theme={null}
    cellDecorators:
      rpn: |
        function(info){
          var val = info.value;
          $(info.cell).toggleClass('boldCol', true);
          $(info.cell).toggleClass('rpn1', val > 0 && val <= 150);
          $(info.cell).toggleClass('rpn2', val > 150 && val <= 250);
          $(info.cell).toggleClass('rpn3', val > 250);
        }
    ```

    This decorator:

    1. Always applies `boldCol` (the second argument `true` means "always add")
    2. Applies `rpn1` only when the value is between 1 and 150
    3. Applies `rpn2` only when the value is between 151 and 250
    4. Applies `rpn3` only when the value exceeds 250
    5. When a condition is `false`, `toggleClass` removes that class, so only one threshold class is ever active on a cell

    <Warning title="Use `toggleClass`, not inline styles">
      Cell decorators must apply classes via `$(info.cell).toggleClass(...)` rather than setting inline styles. Risksheet reuses cell DOM elements as the user scrolls and edits, so a class added by a previous render is removed automatically when `toggleClass` is called with a `false` condition. Inline styles are not cleaned up and produce visual artifacts after edits.
    </Warning>

    ### Row Header Decorator

    You can also apply conditional formatting to row headers using the `headers.rowHeader.renderer` property. The row header decorator accesses data from the entire row:

    ```yaml theme={null}
    cellDecorators:
      rowHeaderRpnNew: |
        function(info){
          var val = info.item['rpnNew'];
          $(info.cell).toggleClass('rpn1', val > 0 && val <= 150);
          $(info.cell).toggleClass('rpn2', val > 150 && val <= 250);
          $(info.cell).toggleClass('rpn3', val > 250);
        }
    headers:
      rowHeader:
        renderer: rowHeaderRpnNew
    ```

    This colors the entire row header based on the revised RPN value, giving an immediate visual indicator of each risk item's current risk level.

    <Note title="Row headers use the same `$(info.cell).toggleClass(...)` form as cells">
      Row-header decorators use the same jQuery `$(info.cell).toggleClass(...)` form as cell-level decorators — this is the form the product's reference templates use, so it keeps your config consistent. (The equivalent function-style call `wijmo.toggleClass(info.cell, 'className', condition)` also works — it converts to the same `$(info.cell).toggleClass(...)` call — but it is not the canonical form.)
    </Note>

    ### The `info` Object in Cell Decorators

    | Property     | Type        | Description                                                 |
    | ------------ | ----------- | ----------------------------------------------------------- |
    | `info.value` | any         | The current cell's display value after formula execution    |
    | `info.cell`  | DOM element | The cell's HTML element (jQuery-wrapped via `$(info.cell)`) |
    | `info.item`  | object      | The full row data object with all column bindings as keys   |

    ### Enum Value Decorator

    When decorating cells that contain enum values, compare against the **enum ID** (the internal identifier), not the display name:

    ```yaml theme={null}
    cellDecorators:
      completionStatus: |
        function(info){
          var val = info.value;
          $(info.cell).toggleClass('statusGreen', val === 'yes');
          $(info.cell).toggleClass('statusRed', val === 'no');
          $(info.cell).toggleClass('statusAmber', val === 'partial');
        }
    ```

    <Warning title="Compare against enum IDs, not display values">
      Cell decorator functions receive the internal enum ID as the value, not the display name. For example, if your enum has `id: "yes"` with `name: "Y"`, the decorator must compare against `'yes'`, not `'Y'`. Comparing against display values is a common mistake that causes conditional formatting to never activate.
    </Warning>

    <Tip title="Use the `rating` type for descriptions in dropdowns">
      If you want enum descriptions to appear in dropdown menus alongside the name, use `type: rating:<enumId>` instead of `type: enum:<enumId>`. Regular enum columns show only the name; rating columns show both name and description. This does not affect how decorators receive the value --- both types pass the enum ID. The underlying enumeration is still defined once in Polarion's Administration area; the column type only changes how Risksheet renders it.
    </Tip>
  </Step>

  <Step title="Apply Static Column Styles">
    For columns that should always have a specific style (not conditional), use the `cellCss` property on the column definition instead of a decorator:

    ```yaml theme={null}
    columns:
      - id: rpn
        header: RPN
        bindings: rpn
        formula: commonRpn
        cellCss: boldCol
    ```

    The `cellCss` property applies the named CSS class to every cell in the column unconditionally. Use it for:

    * Making all cells in a column bold
    * Applying a fixed background color to a column group
    * Adding a border or padding to specific columns

    Combine `cellCss` for static styles with `cellDecorators` for dynamic, value-based styles on the same column.
  </Step>

  <Step title="Highlight Mandatory Fields Visually">
    Risksheet intentionally does not enforce Polarion mandatory field validation during data entry (to make risk entry faster). Instead, use conditional formatting to visually indicate which fields require values:

    ```yaml theme={null}
    cellDecorators:
      severity: |
        function(info){
          var val = info.value;
          $(info.cell).toggleClass('requiredEmpty', !val || val === '');
          $(info.cell).toggleClass('requiredFilled', val && val !== '');
        }
    styles:
      .requiredEmpty: '{background-color: #fff8e1 !important; border-left: 3px solid #ff9800 !important;}'
      .requiredFilled: '{border-left: none;}'
    ```

    <Tip title="Visual indicators for required fields">
      Since Risksheet does not enforce Polarion mandatory fields by design (to simplify data entry), use cell decorators and styles to highlight empty required columns with a colored background or border. This provides a visual cue without blocking the user's workflow.
    </Tip>
  </Step>
</Steps>

## Complete Configuration Example

Here is a full sheet configuration excerpt combining formulas, decorators, and styles for an FMEA risk analysis sheet. The three sections work together: `formulas` compute the values, `cellDecorators` pick a CSS class from the value, and `styles` provide the visual appearance.

```yaml theme={null}
formulas:
  commonRpn: |
    function(info){
      var value = info.item['occ'] * info.item['det'] * info.item['sev'];
      return value ? value : null;
    }
  commonRpnNew: |
    function(info){
      var value = info.item['occNew'] * info.item['detNew'] * info.item['sevNew'];
      return value ? value : null;
    }
cellDecorators:
  rpn: |
    function(info){
      var val = info.value;
      $(info.cell).toggleClass('boldCol', true);
      $(info.cell).toggleClass('rpn1', val > 0 && val <= 150);
      $(info.cell).toggleClass('rpn2', val > 150 && val <= 250);
      $(info.cell).toggleClass('rpn3', val > 250);
    }
  rpnNew: |
    function(info){
      var val = info.value;
      $(info.cell).toggleClass('boldCol', true);
      $(info.cell).toggleClass('rpn1', val > 0 && val <= 150);
      $(info.cell).toggleClass('rpn2', val > 150 && val <= 250);
      $(info.cell).toggleClass('rpn3', val > 250);
    }
  rowHeaderRpnNew: |
    function(info){
      var val = info.item['rpnNew'];
      $(info.cell).toggleClass('rpn1', val > 0 && val <= 150);
      $(info.cell).toggleClass('rpn2', val > 150 && val <= 250);
      $(info.cell).toggleClass('rpn3', val > 250);
    }
styles:
  .boldCol: '{font-weight: 600;}'
  .rpn1: '{background-color: #eaf5e9 !important; color: #1d5f20 !important;}'
  .rpn2: '{background-color: #fff3d2 !important; color: #735602 !important;}'
  .rpn3: '{background-color: #f8eae7 !important; color: #ab1c00 !important;}'
  .firstRow .headSysReq: '{background-color: rgba(62,175,63,0.12) !important; color: #2A792D !important;}'
  .lastRow .headSysReq: '{background-color: #FFF !important; color: #2A792D !important;}'
  .firstRow .headFinalRanking: '{background-color: rgba(62,175,63,0.12) !important; color: #2A792D !important;}'
  .lastRow .headFinalRanking: '{background-color: #FFF !important; color: #2A792D !important;}'
headers:
  rowHeader:
    renderer: rowHeaderRpnNew
  columnHeader:
    height: 32
  columnGroupHeader:
    height: 32
```

## Verification

After applying conditional formatting, you should now see:

1. ✅ RPN cells display with green, amber, or red backgrounds based on their computed values (0--150, 151--250, 251+)
2. ✅ Row headers change color to match the revised RPN risk level
3. ✅ Bold text appears on formula columns with the `boldCol` class
4. ✅ Colors update automatically when severity, occurrence, or detection values change
5. ✅ Conditional formatting is preserved in Excel exports (via `includeStyles`)

<Info title="Verify in application">
  The exact appearance of conditional formatting may vary depending on your browser, Polarion theme, and any custom CSS injected at the project level. Test your decorator functions with representative data covering all threshold ranges (a value just below 150, one between 150 and 250, and one above 250).
</Info>

## See Also

* [Configure Cell Styles](/risksheet/guides/styling/cell-styles) --- static cell styling with the `styles` section
* [Configure Row Header Styles](/risksheet/guides/styling/row-header-styles) --- row header renderers and conditional row coloring
* [Configure Item Colors](/risksheet/guides/styling/item-colors) --- row-level color coding based on work item properties
* [Configure Calculated Columns](/risksheet/guides/columns/calculated-columns) --- formulas that produce the values decorators evaluate
* [Work with Formulas and Hidden Columns](/risksheet/guides/advanced/formulas-hidden-columns) --- formula execution and visibility interaction
* [Create Custom Renderers](/risksheet/guides/customization/custom-renderers) --- advanced cell rendering beyond CSS classes

<LastReviewed date="2026-06-24" />
