To enable CSS support, edit the field options and press the … button next to the HTML option:

This opens a dialog with a set of options, one of which is CSS and provides the ability to edit the stylesheet. A stylesheet contains declarations that map to properties and supported methods in InDesign’s scripting DOM properties.
Working with Text
The InsertionPoint DOM object is used for text based tags to process the CSS. Writable properties can be set where EasyCatalog supports conversion of their value type. Collections are supported as shown in the following example:
1 2 3 | <p style = "characters[all].fillcolor:red;">This is the first paragraph</p> <p style = "characters[all].fillcolor:green;">This is the second paragraph</p> <p style = "characters[all].fillcolor:blue;">This is the third paragraph</p> |
If the HTML contained no formatting:
1 2 3 | <p>This is the first paragraph. It introduces the topic.</p> <p>This is the second paragraph. It provides more details and examples.</p> <p>This is the third paragraph. It concludes the discussion and wraps up the ideas.</p> |
Use a stylesheet to apply properties via a basic CSS parser. For example, nth-of-typeis used here to pick out each of the first 3 paragraphs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | p:nth-of-type(1) { characters[all].fillcolor:red; } p:nth-of-type(2) { characters[all].fillcolor:green; spacebefore: 4pt; } p:nth-of-type(3) { characters[all].fillcolor:blue; texts[0].appliedParagraphStyle:example; } |
Some properties, such as appliedFont, use a tab to separate parts of a value. Use the ^t meta-character sequence for the tab:
1 | <p style="appliedfont:Baskerville^tItalic;">Baskerville Italic</p> |
Working with Images
The Rectangle DOM object is used for <img> tags:
1 2 3 4 5 6 7 8 9 10 11 12 13 | img { fillcolor : orange; strokecolor : yellow; strokeweight: 5pt; topleftcornerradius : 20pt; toprightcornerradius : 20pt; toprightcorneroption : rounded_corner; topleftcorneroption : rounded_corner; transparencysettings.dropshadowsettings.mode : drop; filltint : 90; width : 1cm; height: 1cm; } |
In addition to the DOM properties, EasyCatalog also adds width, height and fit as writable properties. fit accepts contentproportionally and frametocontent.
It’s also possible to manipulate the text inside an image tag, such as forcing the image into a new page or column.
1 2 3 | img { parent.insertionPoints[0].contents : column_break; } |
However because this manipulation is inside the link, the field content is changed so flagged as an error as it modifies content. Modifying link content is generally not recommended for this reason. The following example turns the Rectangle object into a TextFrame and inserts text into it. Meta character sequences are used to insert a paragraph break:
1 2 3 4 5 6 7 8 | img { contenttype : text_type; texts[0].contents : Hello^pWorld; texts[0].justification : center_align; textframePreferences.verticalJustification : center_align; fillcolor : orange; filltint : 50; } |
Working with Tables
When working with tables, the <table> tag uses a Table object. The <tr> tag uses a Row object and the <td><th> tag uses a Cell object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | <body> <table> <thead> <tr> <th>ID</th> <th>Name</th> <th>Age</th> <th>City</th> </tr> </thead> <tbody> <tr> <td>1</td> <td>Alice</td> <td>25</td> <td>New York</td> </tr> <tr> <td>2</td> <td>Bob</td> <td>30</td> <td>London</td> </tr> <tr> <td>3</td> <td>Charlie</td> <td>28</td> <td>Paris</td> </tr> <tr> <td>4</td> <td>Diana</td> <td>32</td> <td>Berlin</td> </tr> <tr> <td>5</td> <td>Edward</td> <td>27</td> <td>Tokyo</td> </tr> </tbody> </table> </body> </html> |
Row Properties
To color the header and alternating rows, the following stylesheets can be defined. The properties here belong to a Row DOM object. Any property that takes a color can also take a css style color name in addition to a named swatch. These are automatically created by EasyCatalog:
1 2 3 4 5 6 7 8 9 10 11 12 | table tr:nth-child(even) { fillcolor: #f2f2f2; /* light gray */ } table tr:nth-child(odd) { fillcolor: #ff0000; /* red */ } th { fillcolor: #000000; /* black */ texts[0].fillcolor: white; } |
Cell Properties
For <td> and <th> tags, a Cell object is used, so properties for the Cell DOM object can be assigned. If the HTML contained a class attribute, eg:
1 | <td class = "age">30</td> |
Then a stylesheet can pick this out:
1 2 3 | td.age { fillcolor: green; } |
Setting a cell width:
1 2 3 | td { width: 2cm; } |
Table Properties
The following HTML contains 2 tables with id attributes to identify them:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | <body> <table id="people"> <thead> <tr> <th>ID</th> <th>Name</th> <th>Age</th> <th>City</th> </tr> </thead> <tbody> <tr> <td>1</td> <td>Alice</td> <td>25</td> <td>New York</td> </tr> <tr> <td>2</td> <td>Bob</td> <td>30</td> <td>London</td> </tr> <tr> <td>3</td> <td>Charlie</td> <td>28</td> <td>Paris</td> </tr> <tr> <td>4</td> <td>Diana</td> <td>32</td> <td>Berlin</td> </tr> <tr> <td>5</td> <td>Edward</td> <td>27</td> <td>Tokyo</td> </tr> </tbody> </table> <table id="prices"> <thead> <tr> <th>Order ID</th> <th>Product</th> <th>Quantity</th> <th>Price ($)</th> <th>Status</th> </tr> </thead> <tbody> <tr> <td>1001</td> <td>Laptop</td> <td>2</td> <td>1200</td> <td>Shipped</td> </tr> <tr> <td>1002</td> <td>Smartphone</td> <td>5</td> <td>800</td> <td>Processing</td> </tr> <tr> <td>1003</td> <td>Headphones</td> <td>10</td> <td>150</td> <td>Delivered</td> </tr> <tr> <td>1004</td> <td>Monitor</td> <td>3</td> <td>300</td> <td>Pending</td> </tr> <tr> <td>1005</td> <td>Keyboard</td> <td>7</td> <td>75</td> <td>Shipped</td> </tr> </tbody> </table> </body> </html> |
The following stylesheet targets specific tables using their ID:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | #people { width: 8cm; } #people td { fillcolor: blue; filltint: 10; } #people th { fillcolor: blue; filltint: 40; } #prices { width: 10cm; } #prices td { fillcolor: red; filltint: 10; } #prices th { fillcolor: red; filltint: 40; } |
This produces the following output:

An HTML style attribute may contain browser CSS values that are unsuitable for the InDesign DOM, such as:
1 | <table border="1" cellpadding="1" cellspacing="1" style="width:500px;"> |
To stop the properties defined by the style node getting applied you can target the node and use the following:
1 2 3 | table { all : unset; /* this stops any class or styles getting applied */ } |
Calling DOM Methods
Supported DOM methods can be called using the method name as the CSS property name. Put the arguments after the colon, separated by commas. Do not add parentheses around the method name or its arguments.
For example, this stores a label on each body cell:
1 2 3 | td { insertlabel: "source", "catalogue"; } |
This corresponds to calling cell.insertLabel("source", "catalogue") in a script. Property and method names are matched without regard to case. If a property and a method share a name, the property takes precedence.
Double quotes preserve commas and spaces within a string. Use \" for a quote inside a quoted argument, and "" to pass an empty string. An empty value passed to the method-argument converter means no arguments; omit trailing optional arguments to let InDesign use their defaults. An empty CSS declaration is ignored by the stylesheet parser, so it cannot be used to call a method with no arguments.
Arguments are converted according to the method’s declared parameter types. Supported conversions include strings, booleans, integers, real numbers, numeric unit values, dates represented numerically, file paths, enums, supported arrays, and records. Parameters declared as Any are passed as strings; EasyCatalog does not infer a number or boolean from their text.
For an enum argument, use its DOM value name, such as right_align, without the enum class prefix. DOM object references, such as a Page or InsertionPoint object, are not currently supported as method arguments. If a parameter also accepts a supported alternative, that alternative can be used. Structs, callbacks and streams are not supported.
Array Values
An array groups several values into one property value or one method argument. Use square brackets and separate the elements with commas.
Arrays in Method Arguments
For example, the move method on a rectangle can take a two-element coordinate array:
1 | move: [72, 144]; |
The array is one argument containing the new [x, y] position. This is equivalent to rectangle.move([72, 144]). The target object must permit the move; placement and anchoring restrictions still apply.
Arrays may contain quoted text, including commas, where the declared element type accepts strings. For example, ["one", "two, three", ""] contains three strings. Use [] for an empty array where the parameter permits it. Fixed-length method arrays must contain the required number of elements. Numeric unit arguments currently require plain numbers; method conversion does not interpret unit strings such as "10mm".
Arrays in Properties
Existing unit, real-number and tab-stop position arrays accept either comma-separated values or an optional outer pair of brackets. For example, these declarations are equivalent for a cell’s gradient starting point:
1 | gradientfillstart: 72, 144; |
1 | gradientfillstart: [72, 144]; |
The gradient starting point is relevant when the cell has a gradient fill. Existing unbracketed CSS continues to use its original conversion behaviour.
Additional supported property-array types include strings, integers, booleans, dates, files, enums and Any. Use bracketed syntax for these types. Elements are converted according to the property’s declared type; Any elements remain strings. These additional array conversions validate fixed lengths and reject the assignment if an element cannot be converted. The existing unit, real-number and tab-stop position handlers retain their previous validation behaviour.
Records and Tab Stops
A record groups named properties using braces. An array of records uses square brackets around a comma-separated list of records. Field names must be unquoted DOM property names. Values use the existing property converters, including units and enum values.
For example, set tab stops in the paragraphs of HTML table cells:
1 2 3 4 5 6 | td, th { paragraphs[all].tablist: [ { position: 12mm, leader: "." }, { position: 24mm, alignment: right_align, leader: "-" } ]; } |
The second tab stop right-aligns text at 24 mm. Quoted strings may contain commas, colons, semicolons or braces. Record fields can be separated by commas or semicolons; do not leave a trailing separator before the closing brace.
For a native table stylesheet using EasyCatalog’s table, row, column and cell selectors, use cell instead of td, th.
The simpler position-only syntax remains supported:
1 2 3 | td, th { paragraphs[all].tablist: 12mm, 24mm, 36mm; } |
The positions can also be written as [12mm, 24mm, 36mm]. Use either the position-only form or records throughout a tab list; do not mix the two forms.
Record fields are checked against the governing DOM object. Unknown, read-only and duplicate fields are rejected. Records are supported only where the DOM declares a record-compatible type; braces are not a general way to create DOM objects. Struct conversion is not currently supported.
Lua Based Property Content
When a property includes [[ and ]], the text enclosed is treated as Lua code, and the result of the code is substituted into the property. A field function can be used within this Lua code to access the contents of any field associated with the current table, cell, row, text range. If the field does not exist, the field function will return nil.
For example, the following code checks the content of the availability field and applies colors to body cells based on its value:
1 2 3 4 5 6 | body cell { fillcolor: [[ if field('availability') == "Now" then return "Red" end return "Green" ]]; } |
Lua Based DOM manipulation
A special lua property exists that enables execution of lua based manipulation of the DOM. The self object is the DOM object that matches the CSS target, so Cell, Table, Row, Column or Text.
For example, here we want a table to be 120 mm wide, with the last column always 35 mm. All other column widths are distributed evenly in the remaining space.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | table { lua: -- Define the target table width local targetTableWidthInPoints = topoints("120mm") -- Last column should be a fixed width local widthOfLastColumnInPoints = topoints("35mm") -- Distribute the remaining space equally -- by setting each column width local totalWidthOfOtherColumns = targetTableWidthInPoints - widthOfLastColumnInPoints local colCount = self:columns():count() local otherColumnWidth = totalWidthOfOtherColumns / (colCount-1) for col = 1, colCount-1 do self:columns():item(col-1):width(otherColumnWidth .. "pt") end self:columns():item(colCount-1):width(widthOfLastColumnInPoints .. 'pt') -- semi colon needed to end the property content ; } |
The following applies color to a text field using the field function, but also uses the lua property to apply an underline using self. All enhanced HTML formatted fields get a surrounding <body> tag by default.
1 2 3 4 5 6 7 8 9 | body { fillcolor: [[ if field('availability') == "Now" then return "Red" end return "Green" ]]; lua: [[ self:underline(field('Availability') == 'Now') ]]; } |
Sharing CSS between fields
If the CSS just contains the name of a file in the data source’s scripts folder, this is loaded and used.
Supported Selectors
The CSS parser is basic and evaluates whether a single HTML-like node matches a given CSS selector. The following selector types are supported:
1. Tag Selector
- Matches elements by their tag name.
- Examples:
div— matches all<div>elements*— wildcard, matches any element
2. ID Selector
- Matches elements by their
idattribute. - Syntax:
#idName - Example:
#main— matches element withid="main"
3. Class Selector
- Matches elements by their
classattribute. - Syntax:
.className - Multiple classes can be chained:
.foo.bar - Matches elements containing all specified classes.
4. Attribute Selectors
- Match elements based on attribute values.
- Supported operators:
[attr=value]— exact match[attr~=value]— whitespace-separated list containsvalue[attr|=value]— equalsvalueor starts withvalue-[attr^=value]— starts withvalue[attr$=value]— ends withvalue[attr*=value]— containsvalue
5. Structural Pseudo-Classes
Target elements based on their position or type among siblings:
| Pseudo-Class | Description |
|---|---|
:first-child | Matches the first child of its parent |
:last-child | Matches the last child of its parent |
:nth-child(n) | Matches the nth child (1-based) of its parent |
:nth-last-child(n) | Matches the nth child from the end |
:first-of-type | Matches the first element of its type among siblings |
:last-of-type | Matches the last element of its type among siblings |
:nth-of-type(n) | Matches the nth element of its type among siblings |
:nth-last-of-type(n) | Matches the nth element of its type from the end |
:only-child | Matches the only child of its parent |
:only-of-type | Matches the only element of its type among siblings |
:empty | Matches elements with no children (no elements, text, or comments) |
Note: nth-child and similar selectors support numeric positions, odd, even and supported an+b formulas. For tag selectors, use forms such as td:nth-child(2), td:nth-child(even) or td:nth-child(2n-1). Formulas containing spaces or a plus sign are supported only through the single-class matching path, for example .price:nth-child(2n + 1).
6. Selector Combinations
table td— descendant selector.table > tbody— direct child selector.tr + tr— adjacent sibling selector.tr ~ tr— general sibling selector.td, th— applies the same declarations to either selector.
Selector Limitations
This is a limited CSS parser, not a browser CSS implementation. Each simple selector supports one attribute selector and one structural pseudo-class. Multiple attribute selectors, chained pseudo-classes, pseudo-elements such as ::before, namespace selectors and escaped selector names are not supported. Attribute comparison modifiers such as [name="value" i] and empty-string comparisons are also unsupported.
The limited :not() support applies to a single class selector containing a supported pseudo-class, for example .price:not(:first-child). It is not a general-purpose selector negation. At-rules such as @media and @keyframes are skipped.
Finding CSS Errors and Warnings
When EasyCatalog logging is enabled, search EasyCatalog Log.txt for these identifiers:
- [CSS ERROR] — reported CSS parsing problems, invalid or unsupported selectors, DOM property and method failures, and method results whose status could not be checked.
- [CSS WARNING] — permissive conversions that accept questionable input while retaining the existing result.
Parser messages include available context such as the property name, selector, or byte offset. Declaration-block offsets are relative to the block being parsed. Stylesheet offsets refer to the parser’s current stylesheet text, which may already have comments removed.
Selector diagnostics are produced when the stylesheet is loaded, with repeated invalid selectors reported once per load. A valid selector that simply matches no objects is not an error. Logging does not change selector matching or the parser’s existing recovery behaviour, and it is not exhaustive CSS validation.
Method-argument conversion errors identify the method, target object, parameter name and position, expected type, and supplied value. If an object type is involved, the message explains that DOM object references cannot be supplied as CSS method arguments. Errors returned by InDesign methods are also reported. If a method supplies an unexpected number of result entries, this is logged without changing the existing success return behaviour.
Conversion warnings include numeric text such as 12abc being accepted as 12, a value outside the 16-bit integer range being narrowed, non-finite real arguments, and an unrecognised boolean such as tru becoming false. These warnings do not cause the conversion to fail. Explicit boolean values include true, false, yes, no, on, off, 1 and 0.