# Multicolumn Pricing Table Widget

> Multi-package pricing comparison table with a shared feature-list column on the left and N package columns on the right. Each package is a Repeater row. Layout previews shipped as SVG/PNG admin images.

**Class file:** `includes/Elements/Multicolumn_Pricing_Table.php` (3,000 lines)
**Slug:** `multicolumn-pricing-table` (widget id `eael-multicolumn-pricing-table`)
**Public docs:** <https://essential-addons.com/elementor/docs/ea-multicolumn-pricing-table/>
**Pro-shared:** Pro-only. Uses Lite's `Classes\Helper`.

## Overview

Renders a comparison-style pricing table — one column lists feature names, additional columns list per-package values (✓ / ✗ / text) for each feature. Packages defined as a Repeater. Multiple layout presets selected via control with admin-image previews (`assets/admin/images/layout-previews/multicolumn-pricing-*`). Optional title marquee animation on package headers.

## Pro vs Lite

Pro-only. Lite has standard Pricing Table widget; Pro adds this multi-column variant (different widget) plus extensions to Lite's via Extender.

## File Map

| File | Role |
| --- | --- |
| `includes/Elements/Multicolumn_Pricing_Table.php` | Widget class (3,000 lines) |
| `src/css/view/multicolumn-pricing-table.scss` → `assets/front-end/css/view/multicolumn-pricing-table.min.css` | Styling |
| `src/js/view/multicolumn-pricing-table.js` → `assets/front-end/js/view/multicolumn-pricing-table.min.js` | Layout switching, marquee |
| `assets/admin/images/layout-previews/multicolumn-pricing-*` | Layout preview images (admin-only) |
| `config.php` entry `'multicolumn-pricing-table'` | Self CSS + self JS |

## Architecture

- **Composes Lite's `Helper`** (line 17) — standard pattern.
- **Repeater for packages** — each row has title / price / features / button / styling overrides.
- **Layout-preview images in admin** — `EAEL_PRO_PLUGIN_URL . 'assets/admin/images/layout-previews/multicolumn-pricing-'` (line 91). Pro uses image-backed `CHOOSE`-style layout picker instead of plain SELECT.
- **Title marquee animation** — class `.title-marquee` triggers CSS `animation-name` (line 195). Optional flair for premium-looking pricing UX.
- **No template directory** — single inline render; layout variations are CSS-driven on the same DOM, not template-include–driven.

## Render Output (canonical)

```html
<div class="eael-multicolumn-pricing-table-wrapper layout-{N} [title-marquee]">
  <!-- Feature list column -->
  <div class="eael-mcpt-feature-list">
    <h3>{feature_column_header}</h3>
    <ul>
      <li>{feature 1}</li>
      <li>{feature 2}</li>
      ...
    </ul>
  </div>
  <!-- One package per Repeater row -->
  <div class="eael-mcpt-package [eael-mcpt-package--featured]">
    [?] <div class="eael-mcpt-package-ribbon">Most Popular</div>
    <div class="eael-mcpt-package-title">{package title}</div>
    <div class="eael-mcpt-package-price">
      <span class="currency">$</span>
      <span class="amount">29</span>
      <span class="period">/mo</span>
    </div>
    <ul class="eael-mcpt-package-features">
      <li class="yes"><span class="icon-yes">✓</span></li>
      <li class="no"><span class="icon-no">✗</span></li>
      <li class="text">Custom value</li>
      ...
    </ul>
    <a class="eael-mcpt-package-button">Buy Now</a>
  </div>
</div>
```

## Controls Reference

| Control id | Tab → Section | Type | Purpose |
| --- | --- | --- | --- |
| `eael_multicolumn_pricing_table_layout` | Content → Layout | CHOOSE (image-backed) | Layout preset |
| Feature list (column header + features Repeater) | Content → Features | TEXT + REPEATER | Feature names |
| Packages Repeater | Content → Packages | REPEATER | Per-package title / price / values / button |
| Per-package features Repeater | Inside Packages Repeater | REPEATER | One entry per feature row: `yes` / `no` / `text` |
| Title marquee | Style → Title | SWITCHER | Enable marquee animation |
| Ribbon (per package) | Inside Packages Repeater | TEXT | "Most Popular" / "Recommended" label |

## Conditional Dependencies

```text
title_marquee = 'yes'
  └── adds .title-marquee class and animation
  └── exposes marquee animation-name/duration controls

Per-package feature value type = 'yes' | 'no' | 'text'
  └── 'yes' / 'no' show icon; 'text' shows custom string
```

## JavaScript Lifecycle

`src/js/view/multicolumn-pricing-table.js` — responsive column switching, marquee animation init if not pure-CSS, possibly equalize-heights helper.

## Hooks & Filters

Standard widget render. No Pro-emitted hooks.

## Common Issues

| Symptom | Likely cause | Diagnose | Fix |
| --- | --- | --- | --- |
| Feature rows misaligned across packages | Different package Repeaters have different feature counts | Inspect each package's nested Repeater | Ensure all packages have the **same number of feature rows in the same order** as the feature list column |
| Marquee not animating | `animation-name` is empty | Inspect computed CSS on `.eael-mcpt-package-title` | Set the marquee animation control value |
| Ribbon overflows package | Ribbon text too long | Inspect `.eael-mcpt-package-ribbon` width | Truncate ribbon text or widen package column |
| Editor preview wrong layout | Layout-preview image cache | Hard-refresh editor | Clear browser cache |
| Mobile layout broken | Columns don't stack | Inspect `@media` breakpoint in SCSS | Add responsive column toggle |

## Known Limitations

- **Feature-row alignment fragility** — packages must independently maintain matching feature-row order; no UI enforcement
- **3,000 lines** — heavy per-layout style controls; refactor candidate
- **No "highlighted package" toggle as control** — must use ribbon manually
- **No prebuilt feature library / templates** — every table is user-built from scratch
- **CSS-driven layout variations** — adding a new layout = new CSS class + admin preview image + control option

## Cross-References

- Sibling: [`pricing-slider.md`](pricing-slider.md), [`price-menu.md`](price-menu.md)
- Lite widget: `Price_Table` (extended by Pro via `Filterable_Gallery_Extender`-style Extender, separate widget)
- Shared patterns: [`_patterns.md`](_patterns.md)
