---
name: syncfusion-grid-css-scope-migration
description: |
  Fix invisible or unstyled elements inside Syncfusion EJ2 Grid cells after migrating from
  DataTables or custom HTML tables. Use when: (1) custom cell content like progress bars,
  badges, or tags render in DOM but have height:0px or no visible styles, (2) CSS rules
  worked in the old table but not in the Grid, (3) computed styles show correct classes
  but zero dimensions. Root cause: CSS selectors scoped to old table wrapper classes
  (e.g. `.backstock-table-wrapper .ageRow`) don't match inside `.e-grid` containers.
author: Claude Code
version: 1.0.0
date: 2026-03-27
---

# Syncfusion EJ2 Grid CSS Scope Migration

## Problem
When migrating from jQuery DataTables (or custom HTML tables) to Syncfusion EJ2 Grid,
custom cell renderers produce HTML with the correct CSS classes, but the elements are
invisible because existing CSS rules are scoped to the old table's wrapper selectors.

The DOM elements exist and have the right class names, but computed styles show
`height: 0px`, `background: transparent`, or other missing properties because the
CSS selectors don't match the new Syncfusion Grid DOM hierarchy.

## Context / Trigger Conditions
- Migrating a table from DataTables/custom HTML to Syncfusion EJ2 Grid
- Custom column templates render HTML with project CSS classes
- Elements appear in DOM inspector but are invisible or unstyled
- `getComputedStyle()` shows `height: 0px` or missing backgrounds
- CSS rules exist but are scoped to old wrapper classes like:
  - `.page-wrapper .table-wrapper .custom-cell { ... }`
  - `.module-home .datatable-container .badge { ... }`

## Solution

### Step 1: Identify the scoping mismatch

Check existing CSS for rules targeting your cell classes. Look for ancestor selectors
that won't exist in the Syncfusion Grid DOM:

```css
/* OLD — scoped to DataTable wrapper */
.backstock-home .backstock-table-wrapper .ageRow .age-progress-bar {
    width: 70px;
    margin: 0 auto var(--space-2);
}
```

The Grid renders inside `<div class="e-grid">`, not inside `.backstock-table-wrapper`.

### Step 2: Add parallel CSS rules for the Grid context

Add new rules that target `.e-grid` as the parent instead of the old wrapper:

```css
/* NEW — scoped to Syncfusion Grid */
.backstock-home .e-grid .age-progress-bar {
    width: 70px;
    margin: 0 auto var(--space-2);
}
```

### Step 3: Keep both rule sets

Don't remove the old rules if legacy pages still use them. The new `.e-grid` rules
coexist safely:

```css
/* Age Progress Bar - Legacy DataTable context */
.backstock-home .backstock-table-wrapper .ageRow .age-progress-bar { ... }

/* Age Progress Bar - Syncfusion Grid context */
.backstock-home .e-grid .age-progress-bar { ... }
```

### Step 4: Rebuild CSS bundle

```bash
php userfrosting/conductor build-css --minify
```

## Verification

1. Reload the page after CSS rebuild
2. Inspect the element — `getComputedStyle()` should show non-zero height/width
3. The element should be visually rendered in the Grid cells

```javascript
// Quick check in browser console
var el = document.querySelector('.e-grid .age-progress-track');
var style = window.getComputedStyle(el);
console.log('height:', style.height, 'bg:', style.backgroundColor);
// Should show non-zero height and correct background color
```

## Example

In the BuyerKiosk backstock module, age progress bars rendered as `height: 0px` after
Grid migration because the CSS was scoped to `.backstock-table-wrapper .ageRow`:

**Before (broken):** Elements in DOM with correct classes, but invisible
```
bar:  { height: "0px", width: "124px" }
track: { height: "0px", background: "rgba(0,0,0,0)" }
fill:  { height: "0px", background: "rgb(34,197,94)" }  // color correct but no height!
```

**After (fixed):** Added `.e-grid` context rules to `backstock.css`
```
bar:  { height: "auto", width: "70px" }
track: { height: "8px", background: "rgb(229,231,235)" }
fill:  { height: "8px", background: "rgb(34,197,94)" }
```

## Notes
- This applies to ANY custom CSS used inside Grid column templates
- Syncfusion Grid creates its own DOM hierarchy: `.e-grid > .e-gridcontent > .e-content > table > tbody > tr.e-row > td`
- Column templates render inside `<td>` cells, so your CSS just needs `.e-grid` as the ancestor
- If you use `rowDataBound` to add classes to `<tr>` elements, those work fine since they're directly on Grid-managed DOM
- Also applies to: badges, tag clouds, status indicators, custom buttons — anything with scoped CSS
- Consider consolidating: if migrating fully, you can replace old selectors instead of duplicating
