Every workbook that paints a cell red when a number gets too low, green when a target is hit, or yellow when something needs review carries a small XML element called <conditionalFormatting> at the bottom of each worksheet. The element looks like styling. It is policy. Each rule encodes a number, a date, a comparison, or a formula that the author considered worth flagging — the discount floor a salesperson must not breach, the margin band that triggers a review, the variance threshold that pages finance, the headcount cap that turns a hiring plan red. The thresholds travel with the file in plain text, the colours are stored in a differential-format table that frequently outlives the cells it once painted, and Document Inspector does not touch any of it. This post walks through how conditional formatting is stored, why every rule is a piece of organisational policy in machine-readable form, how icon sets and colour scales encode KPI targets, how custom-formula rules embed entire approval logics, and how to actually rewrite the layer before workbooks leave the organisation.
Open any XLSX as a ZIP archive and look at xl/worksheets/sheet1.xml. After the cell rows and the data-validation block, before any drawings or extension lists, you will usually find one or more blocks that look like this:
// xl/worksheets/sheet1.xml — conditionalFormatting block
<conditionalFormatting sqref="F2:F500">
<cfRule type="cellIs" dxfId="3" priority="1"
operator="lessThan">
<formula>0.18</formula>
</cfRule>
<cfRule type="cellIs" dxfId="1" priority="2"
operator="greaterThanOrEqual">
<formula>0.32</formula>
</cfRule>
</conditionalFormatting>
Three things are happening here. The sqref attribute names the cell range the rules apply to. The type attribute selects the rule family — cellIs here, but the schema also supports expression, colorScale, dataBar, iconSet, top10, aboveAverage, duplicateValues, timePeriod, and several others. The dxfId attribute is an index into a differential-format table stored in xl/styles.xml, and the priority attribute decides which rule wins when several apply to the same cell.
The two numbers in the example above are the entire story. Somebody at the organisation decided that a value in column F below 0.18 is bad enough to paint red and a value at or above 0.32 is good enough to paint green. Whether column F currently holds the values 0.21, 0.27, and 0.30 is almost irrelevant: the bands themselves are what the rule encodes. The cells can be deleted, the rows can be sorted, the data can be replaced wholesale; the two thresholds remain in worksheet XML as the author’s judgment about where the line is.
A two-rule band that paints red below 18% and green at or above 32% tells a reader, without any ambiguity, that the organisation considers a margin under 18% unacceptable, between 18% and 32% acceptable but not celebrated, and at or above 32% the target. The numbers are not approximations, they are not modelling assumptions, they are commitments somebody made about where the lines go. The bands travel with the workbook forever once typed.
The dxfId attribute on every cfRule indexes into a table of differential formats stored at xl/styles.xml inside <dxfs>. A differential format is a delta against whatever the cell’s base style was: a font colour, a fill, a border, a number format, or a combination. The table looks like this:
// xl/styles.xml — differential format table
<dxfs count="4">
<dxf>
<font><color rgb="FF006100"/></font>
<fill><patternFill><bgColor rgb="FFC6EFCE"/></patternFill></fill>
</dxf>
<dxf>
<font><color rgb="FF9C0006"/></font>
<fill><patternFill><bgColor rgb="FFFFC7CE"/></patternFill></fill>
</dxf>
</dxfs>
The table is shared. Two rules that both paint a cell green typically share a single dxf entry by index. The colour choices encode their own kind of information: the canonical Excel red FFFFC7CE and green FFC6EFCE are the defaults offered by the conditional-formatting wizard, and seeing those exact hex values tells a reader the workbook was authored interactively in Excel itself rather than written by a third-party generator. Custom hex values tend to come from corporate brand palettes pasted out of marketing decks, so a workbook whose dxf entries use the company’s house red is, in passing, a small fingerprint of where it was made.
The table is also leaky. Like the entries in the shared-strings table, dxf entries are not garbage collected when the rules that referenced them are deleted. A workbook whose visible cells carry no conditional formatting at all can still have a populated <dxfs> block in xl/styles.xml — preserving the entire colour palette of a previous version of the file. The orphan entries are durable evidence that conditional formatting once existed in cells that no longer have it.
A workbook with no <conditionalFormatting> elements anywhere in any worksheet but a non-empty <dxfs> block in xl/styles.xml is telling the reader that conditional formatting was removed in a pre-share cleanup. The dxf entries persist as a colour archive of what the workbook used to look like — sometimes including custom brand colours that should not be travelling with externally-shared files at all.
The iconSet rule type paints a small symbol next to each cell value — a green check, a yellow exclamation, a red cross, a set of traffic lights, a row of arrows. The cut points between the icons are the policy. The XML stores them like this:
// three-traffic-light icon set on a KPI column
<conditionalFormatting sqref="H2:H200">
<cfRule type="iconSet" priority="1">
<iconSet iconSet="3TrafficLights1">
<cfvo type="num" val="0"/>
<cfvo type="num" val="0.92"/>
<cfvo type="num" val="1.05"/>
</iconSet>
</cfRule>
</conditionalFormatting>
The three cfvo entries (conditional formatting value objects) define the bands. A reader has just learned that values below 0.92 earn a red light, between 0.92 and 1.05 a yellow light, and at or above 1.05 a green light. In context — a column called “Attainment” or “Performance Index” on a sales spreadsheet — the numbers are exactly the bands the organisation uses to decide who is on plan, who is at risk, and who is overperforming. The XML carries the bands explicitly; the visible cell shows only the resulting icon.
Five-icon sets are even more revealing. The five cut points typically decode into a complete performance scale — strong fail, fail, on track, exceed, strong exceed — and the numbers between them are the exact percentiles or thresholds the organisation uses internally. A workbook with a five-arrow icon set on its revenue forecast column is shipping the entire forecast-band methodology to the recipient as four numbers in plain text.
The cfvo type attribute is also informative. num values are absolute numeric thresholds; percent values are positions within the data range; percentile values are statistical positions; formula values reference other cells. Each one tells a different story about how the author thinks about the band: numeric cuts are policy, percentile cuts are statistical fairness, formula cuts indicate a band whose location depends on something else in the workbook.
colorScale rules paint cells along a colour gradient interpolated between two or three control points, and dataBar rules paint a horizontal bar inside each cell whose length is proportional to the value. Both store their endpoints as cfvo entries, and both leak the author’s sense of what counts as a meaningful range:
// three-colour scale with explicit endpoints
<cfRule type="colorScale" priority="1">
<colorScale>
<cfvo type="num" val="-25000"/>
<cfvo type="num" val="0"/>
<cfvo type="num" val="75000"/>
<color rgb="FFF8696B"/>
<color rgb="FFFFEB84"/>
<color rgb="FF63BE7B"/>
</colorScale>
</cfRule>
The three numbers tell a reader that the column being formatted is expected to range somewhere between a loss of 25,000 and a gain of 75,000, with break-even as the midpoint. The asymmetry is itself information: a colour scale anchored at -25000 and 75000 tells the reader the author considers a 75k upside more typical than a 25k downside, and that break-even matters enough to be the midpoint of the gradient. Replace the column with any other data and the gradient anchors stay put; they describe what the author expected to see, not what the recipient is seeing.
Data bars carry the same kind of metadata in a slightly different form. The two cfvo entries define the minimum and maximum bar lengths; type="num" values fix absolute endpoints, while type="min" and type="max" auto-scale to the data. An auto-scaled bar is the “just visualise it” case and carries little information; a bar with hard-coded endpoints is the case where the author insisted on a specific reference scale and is therefore the more revealing one.
The most expressive rule type is expression, whose <formula> child accepts any Excel formula that resolves to TRUE or FALSE. Custom-formula rules are the place where authors encode the entire business logic that they wanted to be able to see at a glance:
// row flagged red when discount exceeds tier-specific floor
<conditionalFormatting sqref="A2:L500">
<cfRule type="expression" dxfId="5" priority="1">
<formula>$G2>VLOOKUP($D2,DiscountFloors!$A:$B,2,FALSE)</formula>
</cfRule>
</conditionalFormatting>
The reader has just learned that there is a sheet called DiscountFloors in the workbook, that it carries a two-column table of tiers and their corresponding floor discounts, that column G of the active sheet is the actual discount given on a deal, that column D is the customer tier, and that any deal whose discount exceeds the floor for its tier should be painted with whatever the dxfId="5" style is — usually red. The formula is structurally a policy: it is the rule by which the organisation defines an out-of-policy discount. Anyone who reads the XML reads the rule.
Custom-formula rules also frequently encode entire seniority hierarchies. A row painted gold for $E2="Director", silver for $E2="Manager", and bronze for $E2="IC" tells a reader the organisation’s exact role taxonomy without ever showing the actual data. A rule that paints any row whose $P2 contains the substring “Legal Hold” tells a reader the workbook tracks litigation status — even if the column itself has been emptied.
Treat every expression-type rule as if it were a paragraph in the company handbook. The formula encodes a policy: which deals are out of bounds, which rows are flagged for review, which combinations of values get escalated. The recipient does not need any of those policies to use the data, and frequently learns more about how the organisation operates from the conditional-formatting formulas than from the cell values themselves.
Several rule types encode statistical or temporal cuts whose parameters are themselves revealing. top10 rules flag the top or bottom N values (or percentile); aboveAverage rules flag values one or more standard deviations away from the mean; timePeriod rules flag cells whose date falls in a specific calendar window such as “last 7 days” or “next month”. The XML records the exact parameter:
// flag the bottom 5% of opportunities for win-rate review
<cfRule type="top10" dxfId="2" priority="1"
bottom="1" percent="1" rank="5"/>
// flag values more than two standard deviations above the mean
<cfRule type="aboveAverage" dxfId="4" priority="2"
aboveAverage="1" equalAverage="0" stdDev="2"/>
// flag rows whose date falls in the last 7 days
<cfRule type="timePeriod" dxfId="0" priority="3"
timePeriod="last7Days"/>
The top10 rule tells a reader that the author cares about the worst-performing 5% of opportunities specifically — not the bottom decile, not the bottom quartile, exactly the bottom 5%. The aboveAverage rule with stdDev="2" tells a reader the organisation’s anomaly threshold is two sigmas, not three sigmas or one. The timePeriod rule tells a reader the active monitoring window is a rolling week. Each parameter is a small operational choice that the recipient now knows about. None of those choices need to travel with a file going out the door.
The original OOXML conditional-formatting schema does not let a rule reference cells on another sheet directly. Excel 2010 introduced an extension under the x14 namespace that adds cross-sheet support, custom icon sets, and a handful of other features. When a rule needs the extension, Excel writes a stub <cfRule> in the legacy block and the full rule in an extLst entry at the end of the worksheet:
// at the bottom of xl/worksheets/sheet1.xml
<extLst>
<ext xmlns:x14="...x14...">
<x14:conditionalFormattings>
<x14:conditionalFormatting>
<x14:cfRule type="expression" id="{a4...}">
<xm:f>Budget!$D5<Actuals!$D5*0.9</xm:f>
</x14:cfRule>
<xm:sqref>D5:D200</xm:sqref>
</x14:conditionalFormatting>
</x14:conditionalFormattings>
</ext>
</extLst>
The extension carries cross-sheet citations that are not available in the legacy block at all. The example above tells a reader there are two sheets called Budget and Actuals, that column D on each carries the line items the rule compares, and that the organisation considers an actual coming in below 90% of budget worth flagging. The two sheet names themselves are organisational vocabulary; the 0.9 is the variance threshold; the column letters fix the data layout. A workbook that has had its Budget sheet deleted but still carries the extension rule is, again, leaving an orphan citation that proves the deleted sheet existed.
The x14 extension is also exactly the kind of element that older or third-party tooling does not always understand. Mismatches between legacy and extension blocks — a stub in the legacy block with no corresponding extension entry, or vice versa — are reliable fingerprints of conversion pipelines, macro-based exporters, and minimal XLSX writers, in the same way we saw with the shared-strings table and the data-validation layer.
Microsoft’s Document Inspector removes core-properties author names, comment threads, hidden sheets, ink annotations, and several other layers we have covered in previous posts. It does not remove conditional formatting. The justification, which is the same as for data validation, is that conditional formatting is functional — the colour-coding is part of how the workbook is meant to look, not part of its history. The user might want the recipient to see the same red/yellow/green bands they see.
That reasoning collapses the moment the workbook is shared externally. A client receiving a project tracker does not need the consultancy’s internal variance threshold; a regulator receiving a return does not need the firm’s anomaly cut-off; a competitor who buys a sample workbook off an unlucky ex-employee does not need the entire discount-floor table encoded as VLOOKUP-driven rules. The colours are useful to the original team and are pure leakage to anyone else. Document Inspector simply does not draw that distinction, so an “inspected” workbook generally still carries every conditional-formatting rule it was authored with, intact, in worksheet XML.
Workbooks shared after a successful Document Inspector pass routinely still contain every band, every cut point, every gradient anchor, every custom-formula approval rule, and every orphan dxf entry that the file was ever authored with. If your share workflow stops at Document Inspector, the conditional-formatting layer is one of the largest categories of business logic still on its way out the door.
The whole conditional-formatting layer can be enumerated with a few lines of Python. The simplest approach unzips the XLSX, parses each worksheet XML, and walks the rule elements:
# enumerate every conditional formatting rule in every worksheet
import zipfile, re
from lxml import etree
NS = {"a": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"}
with zipfile.ZipFile("workbook.xlsx") as z:
for name in z.namelist():
if not re.match(r"xl/worksheets/sheet\d+\.xml", name): continue
tree = etree.fromstring(z.read(name))
for cf in tree.iter("{a}conditionalFormatting"):
sqref = cf.get("sqref")
for rule in cf.findall("{a}cfRule"):
f = [e.text for e in rule.findall("{a}formula")]
print(name, sqref, rule.get("type"), rule.get("operator"), f)
Run on a typical workbook the script prints one line per rule: the sheet, the cell range, the rule type, the operator, and the formulas that define the threshold. Anyone with five minutes and a Python install can extract every cut point and every business-rule formula from a shared workbook without ever opening Excel. The openpyxl library exposes the same information through worksheet.conditional_formatting and resolves dxf indices into the colour and font deltas they describe, which is the right starting point for automated audits.
For ad-hoc inspection the simplest approach is to rename the XLSX to .zip, expand it, and grep for cfRule across the unzipped tree. Every rule in every worksheet, plus every x14-extension entry, is right there in plain UTF-8 XML. The same workflow is what an opportunistic investigator runs the moment a workbook lands in their inbox.
In a single sweep through the conditional-formatting layer of a moderately-sized workbook, a reader typically extracts:
cellIs rules with lessThan, greaterThan, and between operators.top10, aboveAverage, and timePeriod rules — the bottom-5% review window, the two-sigma anomaly threshold, the rolling-week monitoring scope.x14 extension rules that name budget sheets, target sheets, master-data sheets, and discount-floor tables, including any whose underlying sheets have since been deleted.dxf entries in xl/styles.xml that survive rule deletion and preserve the colour palette of a previous version of the file.The first six categories are policy. The last three are organisational fingerprints. Together they typically tell a recipient more about how the originating organisation measures, evaluates, and escalates than the workbook’s visible content does — because the rules describe what the author cares about, which is usually a sharper signal than what they happened to type into the cells.
Excel exposes a Home > Conditional Formatting > Clear Rules > Entire Sheet command that removes rules from the active sheet, but it does not touch other sheets, does not remove orphan dxf entries from xl/styles.xml, and does not touch the x14 extension block. The reliable path is to operate at the XML level. The minimum cleanup pass for a workbook bound for external release is:
# strip every conditional formatting block and orphan dxf entry
import zipfile, shutil, re
from lxml import etree
NS = "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
def strip_conditional_formatting(src, dst):
shutil.copy(src, dst)
with zipfile.ZipFile(dst, "a") as z:
parts = [n for n in z.namelist()
if re.match(r"xl/worksheets/sheet\d+\.xml", n)]
for part in parts:
tree = etree.fromstring(z.read(part))
for cf in tree.findall(f"{{NS}}conditionalFormatting"):
cf.getparent().remove(cf)
# also strip x14 extension cf entries
for ext in tree.iter(f"{{NS}}ext"):
if "conditionalFormatting" in etree.tostring(ext, encoding="unicode"):
ext.getparent().remove(ext)
z.writestr(part, etree.tostring(tree, xml_declaration=True, encoding="UTF-8"))
# rewrite styles.xml with an empty dxfs block
styles = etree.fromstring(z.read("xl/styles.xml"))
for dxfs in styles.findall(f"{{NS}}dxfs"):
dxfs.getparent().remove(dxfs)
z.writestr("xl/styles.xml", etree.tostring(styles, xml_declaration=True, encoding="UTF-8"))
The script removes every <conditionalFormatting> block, every x14 extension entry that contains conditional formatting, and the entire <dxfs> table from the styles part. Excel opens the resulting file without complaint and shows no coloured bands, icons, or data bars. There is no functional behaviour that has to be preserved for a workbook being shipped externally; the value of red/yellow/green colour-coding in someone else’s copy is essentially zero, and the cost of leaving the rules in place is the entire leak surface this post has described.
A more conservative variant keeps rules whose type is something like duplicateValues — the “highlight duplicates” case carries little policy — and strips everything that encodes a numeric threshold, an icon set cut, a colour-scale anchor, or a custom formula. The split is a one-line filter on rule.get("type"). For most external shares the aggressive variant is correct: colour-coding is an authoring convenience that recipients do not need.
<cfRule> in every worksheet, including the x14 extension block.dxfId back to its dxf entry in xl/styles.xml; confirm no orphan dxf entries remain after rule deletion.expression formula as if it were a paragraph in the company handbook; redact anything that encodes a business rule or seniority hierarchy.Stripping conditional formatting in isolation is necessary but not sufficient. The layer interlocks with several others that have to be touched in the same pass. The defined-names symbol table frequently carries references that were used only by cf rules; those names should be deleted at the same time, both because they are still readable on their own and because orphan names raise no warnings inside Excel itself. Data-validation rules often share cfvo-style threshold values with conditional-formatting rules, and the two layers should be stripped together to avoid telling the recipient through validation what the cleaned cf rules used to say.
The same audit pass should look at the calculation chain, the shared-strings table, the external-links table, the pivot caches, and the Power Query connections, all of which can carry references that the conditional-formatting cleanup just orphaned. The right mental model is that conditional formatting is one of perhaps a dozen metadata layers that share references and identifiers with each other; stripping any one of them without coordinating the others leaves a workbook that has visibly been edited rather than a workbook that is genuinely clean.
Conditional formatting looks like aesthetics and ends up acting like documentation of how the organisation thinks. Every threshold captures a number that somebody considered worth defending. Every icon-set cut captures the line between adequate and inadequate performance. Every colour-scale anchor describes the range of values the author expected to see. Every custom formula encodes an approval rule, a comparison, or a flag that the team uses to decide whether a row deserves attention. The XML stores all of that verbatim, side by side with the cell data, and treats none of it as metadata to be redacted on the way out.
For a recipient of the file the layer is not noise. It is a compact, machine-readable description of the originating organisation’s standards, thresholds, escalation rules, and analytical conventions — written by the people best placed to know all of those things, and shipped without modification. The single most effective change a team can make to its pre-share workflow is to treat conditional formatting as content rather than chrome: review every rule the way they review every cell, and strip the rules that the recipient does not need to see.
For workbooks staying inside an organisation, conditional formatting is exactly what it was designed to be: a small, helpful visual layer that helps a reader find the cells that matter. For workbooks crossing the perimeter, it is one of the most concentrated pieces of organisational judgment the file is ever going to carry. The only durable defence is to rewrite the layer as part of a coordinated cleanup pass that touches the cf rules, the dxf entries they reference, the defined names they relied on, and the master-data sheets they pointed at — leaving recipients with the values the author intended to disclose and none of the bands, cut points, or approval rules they thought no one would ever see.
Use MetaData Analyzer to enumerate every conditional-formatting rule, resolve every dxf reference, surface orphan colour-table entries that survived rule deletion, read every threshold and custom-formula approval rule at the XML level, and confirm the conditional-formatting layer is empty before your workbooks leave the organisation.
The companion layer to conditional formatting — dropdown rules that share threshold values and cross-sheet references with cf rules.
How defined names act as durable references for cf rules and survive sheet deletion as orphan entries in workbook.xml.
Another XLSX layer where deleted content survives in plain text and needs an explicit rewrite to actually disappear.