# EspoCRM Customization: Variable Column Widths for Markdown Tables Instructions for Claude Code. Goal: integrate a custom CSS file into the EspoCRM provisioning so that Markdown tables in text fields get content-based column widths instead of equal fixed widths. The EspoCRM installation is generated by a shell script into a Podman pod, so **both files below must be created by the provisioning script** — manual edits inside the pod would be lost on the next provisioning run. ## Problem and Root Cause EspoCRM renders Markdown in text fields (e.g. Opportunity → `Beschreibung`) inside `.complex-text` containers. The core stylesheet contains this rule (verified live on this instance, EspoCRM 9.3.8, 2026-06-06): ```css .alert table, .alert > .message table, .complex-text table, .confirm-message table, .popover-content table { table-layout: fixed; width: 100%; } ``` `table-layout: fixed` distributes the table width equally across all columns regardless of content. Measured effect on a 4-column table in a record detail view: every column 200 px wide, causing short columns (`Nr.`, `Bew.`) to waste space and the long `Anforderung` column to wrap into unnecessarily tall rows. With `table-layout: auto`, columns size to their content. Verified by live style injection on the test record: column widths became 37 / 112 / 49 / 604 px — exactly the desired result. ## Solution: EspoCRM Custom CSS Mechanism EspoCRM's official mechanism for custom stylesheets ([docs](https://docs.espocrm.com/development/custom-css/)): register the CSS file in the `cssList` metadata. Both involved directories (`client/custom/`, `custom/`) are upgrade-safe — EspoCRM upgrades do not touch them. The CSS filename is arbitrary; we use the declarative name `table-var-col.css`. ### File 1 (new): `/client/custom/css/table-var-col.css` ```css /** * Variable column widths for Markdown tables in text fields. * * EspoCRM core CSS forces `table-layout: fixed; width: 100%` on * `.complex-text table`, which makes all columns equally wide. * `auto` lets columns size to content instead. * * Registered via custom/Espo/Custom/Resources/metadata/app/client.json (cssList). */ .complex-text table { table-layout: auto !important; width: auto !important; max-width: 100% !important; } ``` Notes: - The selector deliberately targets only `.complex-text` (text fields on record views), not the alert/popover tables of the core rule. - `!important` guards against load-order variations; `cssList` entries are appended after the theme stylesheet, but this makes the override robust either way. ### File 2 (new or merge): `/custom/Espo/Custom/Resources/metadata/app/client.json` ```json { "cssList": [ "__APPEND__", "client/custom/css/table-var-col.css" ] } ``` **IMPORTANT — merge, do not overwrite:** This instance already has GUI customizations. If `client.json` already exists, append the path to the existing `cssList` array (keeping `"__APPEND__"` as its first element) and leave all other keys untouched. Check first. ## Integration into the Provisioning Script 1. Locate where the script already injects EspoCRM GUI customizations and follow the same convention (e.g. `podman cp`, volume mount, or heredoc). 2. Create both files after the EspoCRM installation step. 3. Match file ownership/permissions to the other EspoCRM application files (typically the webserver user inside the container, e.g. `www-data:www-data`). The CSS file must be readable by the webserver. 4. Clear the cache afterwards — `cssList` is metadata and is cached: ```bash podman exec php /var/www/html/clear_cache.php # or, if the script already performs a rebuild: podman exec php /var/www/html/rebuild.php ``` (Adapt container name and EspoCRM root path to the script's actual values.) ## Testing 1. **Persistence test (the actual requirement):** re-run the provisioning script from scratch, then verify both files exist inside the pod: `podman exec ls -l /var/www/html/client/custom/css/table-var-col.css /var/www/html/custom/Espo/Custom/Resources/metadata/app/client.json` 2. Log in to the CRM, hard-reload (`Ctrl+F5`). In DevTools → Network, filter for `table-var-col` → the stylesheet must load with HTTP 200. 3. Open the test record (4-column Markdown table in `Beschreibung`): `https://crm.creature-go.com/#Opportunity/view/6a23fc99e2391e7be` 4. Visual check: columns `Nr.`, `Einordnung`, `Bew.` at minimal width, `Anforderung` takes the remaining space; rows are one to two lines instead of uniformly tall. 5. Programmatic check in the DevTools console: ```js getComputedStyle(document.querySelector('.complex-text table')).tableLayout // expected: "auto" (without the fix: "fixed") ``` 6. Regression check: open a record whose description contains no table and one with other Markdown (lists, headings) — rendering must be unchanged. Existing GUI customizations must still be active.