[{"data":1,"prerenderedAt":2422},["ShallowReactive",2],{"doc:\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fhighlight-an-entire-row-based-on-a-cell-value-with-openpyxl":3,"surround:\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fhighlight-an-entire-row-based-on-a-cell-value-with-openpyxl":2390,"ancestors:\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fhighlight-an-entire-row-based-on-a-cell-value-with-openpyxl":2399,"children:\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fhighlight-an-entire-row-based-on-a-cell-value-with-openpyxl":2405,"related:\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fhighlight-an-entire-row-based-on-a-cell-value-with-openpyxl":2406},{"id":4,"title":5,"body":6,"dateModified":2357,"datePublished":2357,"description":2358,"extension":2359,"faq":2360,"keyword":2369,"kind":2370,"meta":2371,"navigation":326,"path":2380,"seo":2381,"slug":2383,"stem":2384,"topics":2385,"type":2388,"__hash__":2389},"docs\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fhighlight-an-entire-row-based-on-a-cell-value-with-openpyxl\u002Findex.md","Highlight an Entire Row Based on a Cell Value with openpyxl",{"type":7,"value":8,"toc":2343},"minimark",[9,33,265,270,295,298,594,598,614,820,837,928,932,939,1215,1222,1226,1241,1394,1407,1486,1490,1497,1617,1627,1631,1641,1856,1866,1870,1873,2106,2122,2126,2231,2235,2245,2249,2269,2273,2279,2285,2291,2297,2301,2339],[10,11,12,13,17,18,22,23,26,27,32],"p",{},"Highlighting a single cell above a threshold is the conditional formatting example everyone knows. What people\nusually want in a tracker or an invoice list is different: the ",[14,15,16],"em",{},"whole row"," turns red when the Status column says\n",[19,20,21],"code",{},"Overdue",", amber when it says ",[19,24,25],{},"Pending",", and grey when the record is closed. Excel does this with a formula rule\napplied across the full width of the table, and the only hard part is getting one dollar sign in the right place.\nThis guide, part of ",[28,29,31],"a",{"href":30},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002F","Applying Conditional Formatting With openpyxl",",\nwrites those rules from Python.",[34,35,43,44,43,48,43,52,43,59,43,67,43,71,43,78,43,82,43,86,43,89,43,93,43,97,43,101,43,104,43,109,43,111,43,115,43,117,43,120,43,122,43,125,43,127,43,130,43,132,43,136,43,140,43,144,43,146,43,149,43,151,43,154,43,156,43,158,43,165,43,170,43,172,43,176,43,178,43,182,43,184,43,187,43,189,43,192,43,194,43,197,43,200,43,204,43,208,43,212,43,214,43,217,43,219,43,222,43,224,43,226,43,229,43,233,43,236,43,240,43,242,43,245,43,247,43,250,43,252,43,255,43,257,43,259],"svg",{"viewBox":36,"role":37,"ariaLabelledBy":38,"xmlns":41,"style":42},"0 0 606 254","img",[39,40],"row-sheet-t","row-sheet-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:606px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[45,46,47],"title",{"id":39},"Rows coloured by the value in the Status column",[49,50,51],"desc",{"id":40},"An invoice list where the row for an overdue invoice is pink across all columns, a pending invoice row is amber, and a paid invoice row is left uncoloured. The colour is driven only by column D.",[53,54],"rect",{"x":55,"y":55,"width":56,"height":57,"fill":58},"0","606","254","#ffffff",[53,60],{"x":61,"y":61,"width":62,"height":63,"rx":55,"fill":64,"stroke":65,"style":66},"20","36","30","#e7ebef","var(--line,#cdd5e6)","stroke-width:1px",[53,68],{"x":69,"y":61,"width":70,"height":63,"rx":55,"fill":64,"stroke":65,"style":66},"56","80",[72,73,77],"text",{"x":74,"y":75,"style":76},"96","40","font-size:11.5px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[53,79],{"x":80,"y":61,"width":81,"height":63,"rx":55,"fill":64,"stroke":65,"style":66},"136","110",[72,83,85],{"x":84,"y":75,"style":76},"191","B",[53,87],{"x":88,"y":61,"width":70,"height":63,"rx":55,"fill":64,"stroke":65,"style":66},"246",[72,90,92],{"x":91,"y":75,"style":76},"286","C",[53,94],{"x":95,"y":61,"width":96,"height":63,"rx":55,"fill":64,"stroke":65,"style":66},"326","90",[72,98,100],{"x":99,"y":75,"style":76},"371","D",[53,102],{"x":61,"y":103,"width":62,"height":63,"rx":55,"fill":64,"stroke":65,"style":66},"50",[72,105,108],{"x":106,"y":107,"style":76},"38","70","1",[53,110],{"x":69,"y":103,"width":70,"height":63,"rx":55,"fill":64,"stroke":65,"style":66},[72,112,114],{"x":74,"y":107,"style":113},"font-size:11.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle","Invoice",[53,116],{"x":80,"y":103,"width":81,"height":63,"rx":55,"fill":64,"stroke":65,"style":66},[72,118,119],{"x":84,"y":107,"style":113},"Customer",[53,121],{"x":88,"y":103,"width":70,"height":63,"rx":55,"fill":64,"stroke":65,"style":66},[72,123,124],{"x":91,"y":107,"style":113},"Amount",[53,126],{"x":95,"y":103,"width":96,"height":63,"rx":55,"fill":64,"stroke":65,"style":66},[72,128,129],{"x":99,"y":107,"style":113},"Status",[53,131],{"x":61,"y":70,"width":62,"height":63,"rx":55,"fill":64,"stroke":65,"style":66},[72,133,135],{"x":106,"y":134,"style":76},"100","2",[53,137],{"x":69,"y":70,"width":70,"height":63,"rx":55,"fill":138,"stroke":139,"style":66},"#fee8f2","var(--accent,#f43f8f)",[72,141,143],{"x":74,"y":134,"style":142},"font-size:11.5px;font-weight:600;fill:var(--accent-ink,#be185d);text-anchor:middle","INV-201",[53,145],{"x":80,"y":70,"width":81,"height":63,"rx":55,"fill":138,"stroke":139,"style":66},[72,147,148],{"x":84,"y":134,"style":142},"Northwind",[53,150],{"x":88,"y":70,"width":70,"height":63,"rx":55,"fill":138,"stroke":139,"style":66},[72,152,153],{"x":91,"y":134,"style":142},"1,250",[53,155],{"x":95,"y":70,"width":96,"height":63,"rx":55,"fill":138,"stroke":139,"style":66},[72,157,21],{"x":99,"y":134,"style":142},[53,159],{"x":160,"y":161,"width":162,"height":163,"rx":164,"fill":138,"stroke":139,"style":66},"436","82","150","26","8",[72,166,169],{"x":167,"y":168,"style":142},"511","99","rule: $D2=\"Overdue\"",[53,171],{"x":61,"y":81,"width":62,"height":63,"rx":55,"fill":64,"stroke":65,"style":66},[72,173,175],{"x":106,"y":174,"style":76},"130","3",[53,177],{"x":69,"y":81,"width":70,"height":63,"rx":55,"fill":58,"stroke":65,"style":66},[72,179,181],{"x":74,"y":174,"style":180},"font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","INV-202",[53,183],{"x":80,"y":81,"width":81,"height":63,"rx":55,"fill":58,"stroke":65,"style":66},[72,185,186],{"x":84,"y":174,"style":180},"Contoso",[53,188],{"x":88,"y":81,"width":70,"height":63,"rx":55,"fill":58,"stroke":65,"style":66},[72,190,191],{"x":91,"y":174,"style":180},"980",[53,193],{"x":95,"y":81,"width":96,"height":63,"rx":55,"fill":58,"stroke":65,"style":66},[72,195,196],{"x":99,"y":174,"style":180},"Paid",[53,198],{"x":61,"y":199,"width":62,"height":63,"rx":55,"fill":64,"stroke":65,"style":66},"140",[72,201,203],{"x":106,"y":202,"style":76},"160","4",[53,205],{"x":69,"y":199,"width":70,"height":63,"rx":55,"fill":206,"stroke":207,"style":66},"#fdefd8","var(--gold,#b4740a)",[72,209,211],{"x":74,"y":202,"style":210},"font-size:11.5px;font-weight:600;fill:var(--gold-ink,#7a4e06);text-anchor:middle","INV-203",[53,213],{"x":80,"y":199,"width":81,"height":63,"rx":55,"fill":206,"stroke":207,"style":66},[72,215,216],{"x":84,"y":202,"style":210},"Fabrikam",[53,218],{"x":88,"y":199,"width":70,"height":63,"rx":55,"fill":206,"stroke":207,"style":66},[72,220,221],{"x":91,"y":202,"style":210},"4,300",[53,223],{"x":95,"y":199,"width":96,"height":63,"rx":55,"fill":206,"stroke":207,"style":66},[72,225,25],{"x":99,"y":202,"style":210},[53,227],{"x":160,"y":228,"width":162,"height":163,"rx":164,"fill":206,"stroke":207,"style":66},"142",[72,230,232],{"x":167,"y":231,"style":210},"159","rule: $D2=\"Pending\"",[53,234],{"x":61,"y":235,"width":62,"height":63,"rx":55,"fill":64,"stroke":65,"style":66},"170",[72,237,239],{"x":106,"y":238,"style":76},"190","5",[53,241],{"x":69,"y":235,"width":70,"height":63,"rx":55,"fill":58,"stroke":65,"style":66},[72,243,244],{"x":74,"y":238,"style":180},"INV-204",[53,246],{"x":80,"y":235,"width":81,"height":63,"rx":55,"fill":58,"stroke":65,"style":66},[72,248,249],{"x":84,"y":238,"style":180},"Litware",[53,251],{"x":88,"y":235,"width":70,"height":63,"rx":55,"fill":58,"stroke":65,"style":66},[72,253,254],{"x":91,"y":238,"style":180},"615",[53,256],{"x":95,"y":235,"width":96,"height":63,"rx":55,"fill":58,"stroke":65,"style":66},[72,258,196],{"x":99,"y":238,"style":180},[72,260,264],{"x":261,"y":262,"style":263},"303","232","font-size:12.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle","one rule per colour, applied to A2:D5",[266,267,269],"h2",{"id":268},"prerequisites","Prerequisites",[271,272,277],"pre",{"className":273,"code":274,"language":275,"meta":276,"style":276},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install openpyxl\n","bash","",[19,278,279],{"__ignoreMap":276},[280,281,284,288,292],"span",{"class":282,"line":283},"line",1,[280,285,287],{"class":286},"sMTad","pip",[280,289,291],{"class":290},"srMev"," install",[280,293,294],{"class":290}," openpyxl\n",[10,296,297],{},"The sample invoice list:",[271,299,303],{"className":300,"code":301,"language":302,"meta":276,"style":276},"language-python shiki shiki-themes github-light github-dark-high-contrast","from openpyxl import Workbook\n\nwb = Workbook()\nws = wb.active\nws.title = \"Invoices\"\nws.append([\"Invoice\", \"Customer\", \"Amount\", \"Status\", \"Due\"])\nrows = [\n    (\"INV-201\", \"Northwind\", 1250, \"Overdue\", \"2026-08-31\"),\n    (\"INV-202\", \"Contoso\", 980, \"Paid\", \"2026-09-05\"),\n    (\"INV-203\", \"Fabrikam\", 4300, \"Pending\", \"2026-09-30\"),\n    (\"INV-204\", \"Litware\", 615, \"Paid\", \"2026-09-12\"),\n    (\"INV-205\", \"Adatum\", 7200, \"Pending\", \"2026-10-02\"),\n]\nfor row in rows:\n    ws.append(row)\nwb.save(\"invoices.xlsx\")\n","python",[19,304,305,321,328,340,351,362,395,406,439,468,498,526,555,561,576,582],{"__ignoreMap":276},[280,306,307,311,315,318],{"class":282,"line":283},[280,308,310],{"class":309},"s-kum","from",[280,312,314],{"class":313},"skGVy"," openpyxl ",[280,316,317],{"class":309},"import",[280,319,320],{"class":313}," Workbook\n",[280,322,324],{"class":282,"line":323},2,[280,325,327],{"emptyLinePlaceholder":326},true,"\n",[280,329,331,334,337],{"class":282,"line":330},3,[280,332,333],{"class":313},"wb ",[280,335,336],{"class":309},"=",[280,338,339],{"class":313}," Workbook()\n",[280,341,343,346,348],{"class":282,"line":342},4,[280,344,345],{"class":313},"ws ",[280,347,336],{"class":309},[280,349,350],{"class":313}," wb.active\n",[280,352,354,357,359],{"class":282,"line":353},5,[280,355,356],{"class":313},"ws.title ",[280,358,336],{"class":309},[280,360,361],{"class":290}," \"Invoices\"\n",[280,363,365,368,371,374,377,379,382,384,387,389,392],{"class":282,"line":364},6,[280,366,367],{"class":313},"ws.append([",[280,369,370],{"class":290},"\"Invoice\"",[280,372,373],{"class":313},", ",[280,375,376],{"class":290},"\"Customer\"",[280,378,373],{"class":313},[280,380,381],{"class":290},"\"Amount\"",[280,383,373],{"class":313},[280,385,386],{"class":290},"\"Status\"",[280,388,373],{"class":313},[280,390,391],{"class":290},"\"Due\"",[280,393,394],{"class":313},"])\n",[280,396,398,401,403],{"class":282,"line":397},7,[280,399,400],{"class":313},"rows ",[280,402,336],{"class":309},[280,404,405],{"class":313}," [\n",[280,407,409,412,415,417,420,422,426,428,431,433,436],{"class":282,"line":408},8,[280,410,411],{"class":313},"    (",[280,413,414],{"class":290},"\"INV-201\"",[280,416,373],{"class":313},[280,418,419],{"class":290},"\"Northwind\"",[280,421,373],{"class":313},[280,423,425],{"class":424},"sP0c6","1250",[280,427,373],{"class":313},[280,429,430],{"class":290},"\"Overdue\"",[280,432,373],{"class":313},[280,434,435],{"class":290},"\"2026-08-31\"",[280,437,438],{"class":313},"),\n",[280,440,442,444,447,449,452,454,456,458,461,463,466],{"class":282,"line":441},9,[280,443,411],{"class":313},[280,445,446],{"class":290},"\"INV-202\"",[280,448,373],{"class":313},[280,450,451],{"class":290},"\"Contoso\"",[280,453,373],{"class":313},[280,455,191],{"class":424},[280,457,373],{"class":313},[280,459,460],{"class":290},"\"Paid\"",[280,462,373],{"class":313},[280,464,465],{"class":290},"\"2026-09-05\"",[280,467,438],{"class":313},[280,469,471,473,476,478,481,483,486,488,491,493,496],{"class":282,"line":470},10,[280,472,411],{"class":313},[280,474,475],{"class":290},"\"INV-203\"",[280,477,373],{"class":313},[280,479,480],{"class":290},"\"Fabrikam\"",[280,482,373],{"class":313},[280,484,485],{"class":424},"4300",[280,487,373],{"class":313},[280,489,490],{"class":290},"\"Pending\"",[280,492,373],{"class":313},[280,494,495],{"class":290},"\"2026-09-30\"",[280,497,438],{"class":313},[280,499,501,503,506,508,511,513,515,517,519,521,524],{"class":282,"line":500},11,[280,502,411],{"class":313},[280,504,505],{"class":290},"\"INV-204\"",[280,507,373],{"class":313},[280,509,510],{"class":290},"\"Litware\"",[280,512,373],{"class":313},[280,514,254],{"class":424},[280,516,373],{"class":313},[280,518,460],{"class":290},[280,520,373],{"class":313},[280,522,523],{"class":290},"\"2026-09-12\"",[280,525,438],{"class":313},[280,527,529,531,534,536,539,541,544,546,548,550,553],{"class":282,"line":528},12,[280,530,411],{"class":313},[280,532,533],{"class":290},"\"INV-205\"",[280,535,373],{"class":313},[280,537,538],{"class":290},"\"Adatum\"",[280,540,373],{"class":313},[280,542,543],{"class":424},"7200",[280,545,373],{"class":313},[280,547,490],{"class":290},[280,549,373],{"class":313},[280,551,552],{"class":290},"\"2026-10-02\"",[280,554,438],{"class":313},[280,556,558],{"class":282,"line":557},13,[280,559,560],{"class":313},"]\n",[280,562,564,567,570,573],{"class":282,"line":563},14,[280,565,566],{"class":309},"for",[280,568,569],{"class":313}," row ",[280,571,572],{"class":309},"in",[280,574,575],{"class":313}," rows:\n",[280,577,579],{"class":282,"line":578},15,[280,580,581],{"class":313},"    ws.append(row)\n",[280,583,585,588,591],{"class":282,"line":584},16,[280,586,587],{"class":313},"wb.save(",[280,589,590],{"class":290},"\"invoices.xlsx\"",[280,592,593],{"class":313},")\n",[266,595,597],{"id":596},"step-1-write-a-formula-rule-across-the-whole-row-range","Step 1 — Write a formula rule across the whole row range",[10,599,600,601,604,605,609,610,613],{},"A ",[19,602,603],{},"FormulaRule"," applies formatting wherever its formula is true. The rule is written for the ",[606,607,608],"strong",{},"top-left cell"," of the range it covers, and Excel adjusts it for every\nother cell exactly as it would copy a formula. Apply it to all columns of the table and test the Status column with ",[19,611,612],{},"$D2",".",[271,615,617],{"className":300,"code":616,"language":302,"meta":276,"style":276},"from openpyxl import load_workbook\nfrom openpyxl.formatting.rule import FormulaRule\nfrom openpyxl.styles import Font, PatternFill\n\nwb = load_workbook(\"invoices.xlsx\")\nws = wb[\"Invoices\"]\nlast_row = ws.max_row\n\noverdue = FormulaRule(\n    formula=['$D2=\"Overdue\"'],\n    fill=PatternFill(\"solid\", start_color=\"FFC7CE\", end_color=\"FFC7CE\"),\n    font=Font(color=\"9C0006\"),\n)\nws.conditional_formatting.add(f\"A2:E{last_row}\", overdue)\nwb.save(\"invoices_highlighted.xlsx\")\n",[19,618,619,630,642,654,658,671,685,695,699,709,726,760,780,784,811],{"__ignoreMap":276},[280,620,621,623,625,627],{"class":282,"line":283},[280,622,310],{"class":309},[280,624,314],{"class":313},[280,626,317],{"class":309},[280,628,629],{"class":313}," load_workbook\n",[280,631,632,634,637,639],{"class":282,"line":323},[280,633,310],{"class":309},[280,635,636],{"class":313}," openpyxl.formatting.rule ",[280,638,317],{"class":309},[280,640,641],{"class":313}," FormulaRule\n",[280,643,644,646,649,651],{"class":282,"line":330},[280,645,310],{"class":309},[280,647,648],{"class":313}," openpyxl.styles ",[280,650,317],{"class":309},[280,652,653],{"class":313}," Font, PatternFill\n",[280,655,656],{"class":282,"line":342},[280,657,327],{"emptyLinePlaceholder":326},[280,659,660,662,664,667,669],{"class":282,"line":353},[280,661,333],{"class":313},[280,663,336],{"class":309},[280,665,666],{"class":313}," load_workbook(",[280,668,590],{"class":290},[280,670,593],{"class":313},[280,672,673,675,677,680,683],{"class":282,"line":364},[280,674,345],{"class":313},[280,676,336],{"class":309},[280,678,679],{"class":313}," wb[",[280,681,682],{"class":290},"\"Invoices\"",[280,684,560],{"class":313},[280,686,687,690,692],{"class":282,"line":397},[280,688,689],{"class":313},"last_row ",[280,691,336],{"class":309},[280,693,694],{"class":313}," ws.max_row\n",[280,696,697],{"class":282,"line":408},[280,698,327],{"emptyLinePlaceholder":326},[280,700,701,704,706],{"class":282,"line":441},[280,702,703],{"class":313},"overdue ",[280,705,336],{"class":309},[280,707,708],{"class":313}," FormulaRule(\n",[280,710,711,715,717,720,723],{"class":282,"line":470},[280,712,714],{"class":713},"sa561","    formula",[280,716,336],{"class":309},[280,718,719],{"class":313},"[",[280,721,722],{"class":290},"'$D2=\"Overdue\"'",[280,724,725],{"class":313},"],\n",[280,727,728,731,733,736,739,741,744,746,749,751,754,756,758],{"class":282,"line":500},[280,729,730],{"class":713},"    fill",[280,732,336],{"class":309},[280,734,735],{"class":313},"PatternFill(",[280,737,738],{"class":290},"\"solid\"",[280,740,373],{"class":313},[280,742,743],{"class":713},"start_color",[280,745,336],{"class":309},[280,747,748],{"class":290},"\"FFC7CE\"",[280,750,373],{"class":313},[280,752,753],{"class":713},"end_color",[280,755,336],{"class":309},[280,757,748],{"class":290},[280,759,438],{"class":313},[280,761,762,765,767,770,773,775,778],{"class":282,"line":528},[280,763,764],{"class":713},"    font",[280,766,336],{"class":309},[280,768,769],{"class":313},"Font(",[280,771,772],{"class":713},"color",[280,774,336],{"class":309},[280,776,777],{"class":290},"\"9C0006\"",[280,779,438],{"class":313},[280,781,782],{"class":282,"line":557},[280,783,593],{"class":313},[280,785,786,789,792,795,799,802,805,808],{"class":282,"line":563},[280,787,788],{"class":313},"ws.conditional_formatting.add(",[280,790,791],{"class":309},"f",[280,793,794],{"class":290},"\"A2:E",[280,796,798],{"class":797},"sSjpA","{",[280,800,801],{"class":313},"last_row",[280,803,804],{"class":797},"}",[280,806,807],{"class":290},"\"",[280,809,810],{"class":313},", overdue)\n",[280,812,813,815,818],{"class":282,"line":578},[280,814,587],{"class":313},[280,816,817],{"class":290},"\"invoices_highlighted.xlsx\"",[280,819,593],{"class":313},[10,821,822,824,825,828,829,832,833,836],{},[19,823,612],{}," is the whole trick. The dollar sign fixes the ",[14,826,827],{},"column",", so every cell in a row — A, B, C, D, E — looks at column D. The row number has no dollar sign, so it moves:\ncells in row 3 test ",[19,830,831],{},"$D3",", row 4 tests ",[19,834,835],{},"$D4",". The colours are Excel's built-in \"Light Red Fill with Dark Red Text\" pair, which keeps text readable.",[34,838,43,844,43,847,43,850,43,853,43,859,43,865,43,868,43,873,43,877,43,881,43,884,43,889,43,893,43,896,43,899,43,902,43,907,43,911,43,915,43,918,43,921,43,924],{"viewBox":839,"role":37,"ariaLabelledBy":840,"xmlns":41,"style":843},"0 0 760 191",[841,842],"row-refs-t","row-refs-d","width:100%;max-width:760px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[45,845,846],{"id":841},"How the reference style decides what gets coloured",[49,848,849],{"id":842},"With D2 relative in both directions, each column tests a different column and only some cells colour. With $D$2 fully absolute, every row tests row 2 so the whole range follows the first record. With $D2, column fixed and row relative, each row tests its own Status cell, which colours entire rows correctly.",[53,851],{"x":55,"y":55,"width":852,"height":84,"fill":58},"760",[53,854],{"x":61,"y":61,"width":855,"height":856,"rx":857,"fill":138,"stroke":139,"style":858},"226.7","127","14","stroke-width:2px",[72,860,864],{"x":861,"y":862,"style":863},"133.3","46","font-size:13px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","D2",[282,866],{"x1":62,"y1":69,"x2":867,"y2":69,"stroke":139,"style":66},"230.7",[72,869,872],{"x":861,"y":870,"style":871},"78","font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","column shifts: B tests E",[72,874,876],{"x":861,"y":875,"style":871},"101","row shifts: correct",[72,878,880],{"x":861,"y":879,"style":871},"124","patchy colouring",[53,882],{"x":883,"y":61,"width":855,"height":856,"rx":857,"fill":206,"stroke":207,"style":858},"266.7",[72,885,888],{"x":886,"y":862,"style":887},"380","font-size:13px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","$D$2",[282,890],{"x1":891,"y1":69,"x2":892,"y2":69,"stroke":207,"style":66},"282.7","477.3",[72,894,895],{"x":886,"y":870,"style":871},"column fixed",[72,897,898],{"x":886,"y":875,"style":871},"row fixed to 2",[72,900,901],{"x":886,"y":879,"style":871},"all rows copy row 2",[53,903],{"x":904,"y":61,"width":855,"height":856,"rx":857,"fill":905,"stroke":906,"style":858},"513.3","#d9f4f1","var(--teal,#0f9488)",[72,908,612],{"x":909,"y":862,"style":910},"626.7","font-size:13px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle",[282,912],{"x1":913,"y1":69,"x2":914,"y2":69,"stroke":906,"style":66},"529.3","724",[72,916,917],{"x":909,"y":870,"style":871},"column fixed to D",[72,919,920],{"x":909,"y":875,"style":871},"row follows each row",[72,922,923],{"x":909,"y":879,"style":871},"whole rows, correctly",[72,925,927],{"x":886,"y":926,"style":263},"177","fix the column, free the row",[266,929,931],{"id":930},"step-2-add-one-rule-per-status","Step 2 — Add one rule per status",[10,933,934,935,938],{},"Each colour is its own rule over the same range. Rules are evaluated in the order they were added, and ",[19,936,937],{},"stopIfTrue"," prevents a later rule from also applying — useful when\nconditions can overlap.",[271,940,942],{"className":300,"code":941,"language":302,"meta":276,"style":276},"def fill(hex_colour: str) -> PatternFill:\n    return PatternFill(\"solid\", start_color=hex_colour, end_color=hex_colour)\n\nrules = [\n    ('$D2=\"Overdue\"', fill(\"FFC7CE\"), Font(color=\"9C0006\")),\n    ('$D2=\"Pending\"', fill(\"FFEB9C\"), Font(color=\"9C5700\")),\n    ('$D2=\"Paid\"', fill(\"F2F2F2\"), Font(color=\"7F7F7F\")),\n]\n\nwb = load_workbook(\"invoices.xlsx\")\nws = wb[\"Invoices\"]\ntarget = f\"A2:E{ws.max_row}\"\nfor formula, rule_fill, rule_font in rules:\n    ws.conditional_formatting.add(\n        target, FormulaRule(formula=[formula], fill=rule_fill, font=rule_font, stopIfTrue=True)\n    )\nwb.save(\"invoices_status.xlsx\")\nprint(len(ws.conditional_formatting[target]), \"rules on\", target)\n",[19,943,944,962,988,992,1001,1024,1047,1070,1074,1078,1090,1102,1124,1136,1141,1179,1184,1194],{"__ignoreMap":276},[280,945,946,949,953,956,959],{"class":282,"line":283},[280,947,948],{"class":309},"def",[280,950,952],{"class":951},"s_Opv"," fill",[280,954,955],{"class":313},"(hex_colour: ",[280,957,958],{"class":424},"str",[280,960,961],{"class":313},") -> PatternFill:\n",[280,963,964,967,970,972,974,976,978,981,983,985],{"class":282,"line":323},[280,965,966],{"class":309},"    return",[280,968,969],{"class":313}," PatternFill(",[280,971,738],{"class":290},[280,973,373],{"class":313},[280,975,743],{"class":713},[280,977,336],{"class":309},[280,979,980],{"class":313},"hex_colour, ",[280,982,753],{"class":713},[280,984,336],{"class":309},[280,986,987],{"class":313},"hex_colour)\n",[280,989,990],{"class":282,"line":330},[280,991,327],{"emptyLinePlaceholder":326},[280,993,994,997,999],{"class":282,"line":342},[280,995,996],{"class":313},"rules ",[280,998,336],{"class":309},[280,1000,405],{"class":313},[280,1002,1003,1005,1007,1010,1012,1015,1017,1019,1021],{"class":282,"line":353},[280,1004,411],{"class":313},[280,1006,722],{"class":290},[280,1008,1009],{"class":313},", fill(",[280,1011,748],{"class":290},[280,1013,1014],{"class":313},"), Font(",[280,1016,772],{"class":713},[280,1018,336],{"class":309},[280,1020,777],{"class":290},[280,1022,1023],{"class":313},")),\n",[280,1025,1026,1028,1031,1033,1036,1038,1040,1042,1045],{"class":282,"line":364},[280,1027,411],{"class":313},[280,1029,1030],{"class":290},"'$D2=\"Pending\"'",[280,1032,1009],{"class":313},[280,1034,1035],{"class":290},"\"FFEB9C\"",[280,1037,1014],{"class":313},[280,1039,772],{"class":713},[280,1041,336],{"class":309},[280,1043,1044],{"class":290},"\"9C5700\"",[280,1046,1023],{"class":313},[280,1048,1049,1051,1054,1056,1059,1061,1063,1065,1068],{"class":282,"line":397},[280,1050,411],{"class":313},[280,1052,1053],{"class":290},"'$D2=\"Paid\"'",[280,1055,1009],{"class":313},[280,1057,1058],{"class":290},"\"F2F2F2\"",[280,1060,1014],{"class":313},[280,1062,772],{"class":713},[280,1064,336],{"class":309},[280,1066,1067],{"class":290},"\"7F7F7F\"",[280,1069,1023],{"class":313},[280,1071,1072],{"class":282,"line":408},[280,1073,560],{"class":313},[280,1075,1076],{"class":282,"line":441},[280,1077,327],{"emptyLinePlaceholder":326},[280,1079,1080,1082,1084,1086,1088],{"class":282,"line":470},[280,1081,333],{"class":313},[280,1083,336],{"class":309},[280,1085,666],{"class":313},[280,1087,590],{"class":290},[280,1089,593],{"class":313},[280,1091,1092,1094,1096,1098,1100],{"class":282,"line":500},[280,1093,345],{"class":313},[280,1095,336],{"class":309},[280,1097,679],{"class":313},[280,1099,682],{"class":290},[280,1101,560],{"class":313},[280,1103,1104,1107,1109,1112,1114,1116,1119,1121],{"class":282,"line":528},[280,1105,1106],{"class":313},"target ",[280,1108,336],{"class":309},[280,1110,1111],{"class":309}," f",[280,1113,794],{"class":290},[280,1115,798],{"class":797},[280,1117,1118],{"class":313},"ws.max_row",[280,1120,804],{"class":797},[280,1122,1123],{"class":290},"\"\n",[280,1125,1126,1128,1131,1133],{"class":282,"line":557},[280,1127,566],{"class":309},[280,1129,1130],{"class":313}," formula, rule_fill, rule_font ",[280,1132,572],{"class":309},[280,1134,1135],{"class":313}," rules:\n",[280,1137,1138],{"class":282,"line":563},[280,1139,1140],{"class":313},"    ws.conditional_formatting.add(\n",[280,1142,1143,1146,1149,1151,1154,1157,1159,1162,1165,1167,1170,1172,1174,1177],{"class":282,"line":578},[280,1144,1145],{"class":313},"        target, FormulaRule(",[280,1147,1148],{"class":713},"formula",[280,1150,336],{"class":309},[280,1152,1153],{"class":313},"[formula], ",[280,1155,1156],{"class":713},"fill",[280,1158,336],{"class":309},[280,1160,1161],{"class":313},"rule_fill, ",[280,1163,1164],{"class":713},"font",[280,1166,336],{"class":309},[280,1168,1169],{"class":313},"rule_font, ",[280,1171,937],{"class":713},[280,1173,336],{"class":309},[280,1175,1176],{"class":424},"True",[280,1178,593],{"class":313},[280,1180,1181],{"class":282,"line":584},[280,1182,1183],{"class":313},"    )\n",[280,1185,1187,1189,1192],{"class":282,"line":1186},17,[280,1188,587],{"class":313},[280,1190,1191],{"class":290},"\"invoices_status.xlsx\"",[280,1193,593],{"class":313},[280,1195,1197,1200,1203,1206,1209,1212],{"class":282,"line":1196},18,[280,1198,1199],{"class":424},"print",[280,1201,1202],{"class":313},"(",[280,1204,1205],{"class":424},"len",[280,1207,1208],{"class":313},"(ws.conditional_formatting[target]), ",[280,1210,1211],{"class":290},"\"rules on\"",[280,1213,1214],{"class":313},", target)\n",[10,1216,1217,1218,1221],{},"Text comparisons in Excel formulas are case-insensitive, so ",[19,1219,1220],{},"\"overdue\""," typed in lower case still matches. That is convenient, and also a reason not to rely on case to\ndistinguish statuses.",[266,1223,1225],{"id":1224},"step-3-highlight-on-numbers-dates-and-combinations","Step 3 — Highlight on numbers, dates and combinations",[10,1227,1228,1229,1232,1233,1236,1237,1240],{},"The formula can be any Excel expression that returns TRUE or FALSE for the row. Numeric thresholds, date comparisons with ",[19,1230,1231],{},"TODAY()",", and combinations with ",[19,1234,1235],{},"AND"," and ",[19,1238,1239],{},"OR","\nall work, as long as each column reference keeps its dollar sign.",[271,1242,1244],{"className":300,"code":1243,"language":302,"meta":276,"style":276},"wb = load_workbook(\"invoices.xlsx\")\nws = wb[\"Invoices\"]\ntarget = f\"A2:E{ws.max_row}\"\n\n# Large pending invoices: both conditions on the same row\nws.conditional_formatting.add(target, FormulaRule(\n    formula=['AND($D2=\"Pending\",$C2>=5000)'], fill=fill(\"FFEB9C\"), font=Font(bold=True)))\n\n# Past due date and not yet paid (Due is stored as text here, so convert it)\nws.conditional_formatting.add(target, FormulaRule(\n    formula=['AND($D2\u003C>\"Paid\",DATEVALUE($E2)\u003CTODAY())'], fill=fill(\"FFC7CE\")))\n\nwb.save(\"invoices_rules.xlsx\")\n",[19,1245,1246,1258,1270,1288,1292,1298,1303,1345,1349,1354,1358,1381,1385],{"__ignoreMap":276},[280,1247,1248,1250,1252,1254,1256],{"class":282,"line":283},[280,1249,333],{"class":313},[280,1251,336],{"class":309},[280,1253,666],{"class":313},[280,1255,590],{"class":290},[280,1257,593],{"class":313},[280,1259,1260,1262,1264,1266,1268],{"class":282,"line":323},[280,1261,345],{"class":313},[280,1263,336],{"class":309},[280,1265,679],{"class":313},[280,1267,682],{"class":290},[280,1269,560],{"class":313},[280,1271,1272,1274,1276,1278,1280,1282,1284,1286],{"class":282,"line":330},[280,1273,1106],{"class":313},[280,1275,336],{"class":309},[280,1277,1111],{"class":309},[280,1279,794],{"class":290},[280,1281,798],{"class":797},[280,1283,1118],{"class":313},[280,1285,804],{"class":797},[280,1287,1123],{"class":290},[280,1289,1290],{"class":282,"line":342},[280,1291,327],{"emptyLinePlaceholder":326},[280,1293,1294],{"class":282,"line":353},[280,1295,1297],{"class":1296},"s-wDw","# Large pending invoices: both conditions on the same row\n",[280,1299,1300],{"class":282,"line":364},[280,1301,1302],{"class":313},"ws.conditional_formatting.add(target, FormulaRule(\n",[280,1304,1305,1307,1309,1311,1314,1317,1319,1321,1324,1326,1329,1331,1333,1335,1338,1340,1342],{"class":282,"line":397},[280,1306,714],{"class":713},[280,1308,336],{"class":309},[280,1310,719],{"class":313},[280,1312,1313],{"class":290},"'AND($D2=\"Pending\",$C2>=5000)'",[280,1315,1316],{"class":313},"], ",[280,1318,1156],{"class":713},[280,1320,336],{"class":309},[280,1322,1323],{"class":313},"fill(",[280,1325,1035],{"class":290},[280,1327,1328],{"class":313},"), ",[280,1330,1164],{"class":713},[280,1332,336],{"class":309},[280,1334,769],{"class":313},[280,1336,1337],{"class":713},"bold",[280,1339,336],{"class":309},[280,1341,1176],{"class":424},[280,1343,1344],{"class":313},")))\n",[280,1346,1347],{"class":282,"line":408},[280,1348,327],{"emptyLinePlaceholder":326},[280,1350,1351],{"class":282,"line":441},[280,1352,1353],{"class":1296},"# Past due date and not yet paid (Due is stored as text here, so convert it)\n",[280,1355,1356],{"class":282,"line":470},[280,1357,1302],{"class":313},[280,1359,1360,1362,1364,1366,1369,1371,1373,1375,1377,1379],{"class":282,"line":500},[280,1361,714],{"class":713},[280,1363,336],{"class":309},[280,1365,719],{"class":313},[280,1367,1368],{"class":290},"'AND($D2\u003C>\"Paid\",DATEVALUE($E2)\u003CTODAY())'",[280,1370,1316],{"class":313},[280,1372,1156],{"class":713},[280,1374,336],{"class":309},[280,1376,1323],{"class":313},[280,1378,748],{"class":290},[280,1380,1344],{"class":313},[280,1382,1383],{"class":282,"line":528},[280,1384,327],{"emptyLinePlaceholder":326},[280,1386,1387,1389,1392],{"class":282,"line":557},[280,1388,587],{"class":313},[280,1390,1391],{"class":290},"\"invoices_rules.xlsx\"",[280,1393,593],{"class":313},[10,1395,1396,1398,1399,1402,1403,613],{},[19,1397,1231],{}," is recalculated whenever the workbook opens, so a due-date rule keeps working long after the file was written — the colours reflect the day someone looks at the\nsheet, not the day the script ran. If the dates are real Excel dates rather than text, compare ",[19,1400,1401],{},"$E2\u003CTODAY()"," directly; converting text dates properly at write time is covered\nin ",[28,1404,1406],{"href":1405},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fapplying-number-and-date-formats-in-excel\u002Fformat-dates-in-excel-cells-with-python\u002F","Format Dates in Excel Cells with Python",[34,1408,43,1413,43,1416,43,1419,43,1421,43,1426,43,1431,43,1436,43,1439,43,1444,43,1447,43,1450,43,1454,43,1457,43,1462,43,1467,43,1470,43,1473,43,1478,43,1482],{"viewBox":1409,"role":37,"ariaLabelledBy":1410,"xmlns":41,"style":843},"0 0 760 380",[1411,1412],"row-formulas-t","row-formulas-d",[45,1414,1415],{"id":1411},"Example row rule formulas and what they mean",[49,1417,1418],{"id":1412},"A text equality on the status column, a numeric threshold on the amount column, an AND combining status and amount, a due date before today for unpaid rows, and a MOD on ROW for banded rows.",[53,1420],{"x":55,"y":55,"width":852,"height":886,"fill":58},[53,1422],{"x":61,"y":61,"width":1423,"height":1424,"rx":1425,"fill":138,"stroke":139,"style":858},"720","52","12",[72,1427,1430],{"x":75,"y":1428,"style":1429},"51","font-size:14px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:start","$D2=\"Overdue\"",[72,1432,1435],{"x":1433,"y":1428,"style":1434},"320","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:start","status equals a word (case-insensitive)",[53,1437],{"x":61,"y":1438,"width":1423,"height":1424,"rx":1425,"fill":206,"stroke":207,"style":858},"84",[72,1440,1443],{"x":75,"y":1441,"style":1442},"115","font-size:14px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:start","$C2>=5000",[72,1445,1446],{"x":1433,"y":1441,"style":1434},"amount at or above a limit",[53,1448],{"x":61,"y":1449,"width":1423,"height":1424,"rx":1425,"fill":206,"stroke":207,"style":858},"148",[72,1451,1453],{"x":75,"y":1452,"style":1442},"179","AND($D2=\"Pending\",$C2>=5000)",[72,1455,1456],{"x":1433,"y":1452,"style":1434},"both conditions on the same row",[53,1458],{"x":61,"y":1459,"width":1423,"height":1424,"rx":1425,"fill":1460,"stroke":1461,"style":858},"212","#f0f4ff","var(--brand,#5b5cf0)",[72,1463,1466],{"x":75,"y":1464,"style":1465},"243","font-size:14px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:start","AND($D2\u003C>\"Paid\",$E2\u003CTODAY())",[72,1468,1469],{"x":1433,"y":1464,"style":1434},"unpaid and past due, re-evaluated daily",[53,1471],{"x":61,"y":1472,"width":1423,"height":1424,"rx":1425,"fill":64,"stroke":65,"style":858},"276",[72,1474,1477],{"x":75,"y":1475,"style":1476},"307","font-size:14px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:start","MOD(ROW(),2)=0",[72,1479,1481],{"x":1433,"y":1475,"style":1480},"font-size:12px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:start","every other row, for banding",[72,1483,1485],{"x":886,"y":1484,"style":263},"360","any TRUE\u002FFALSE formula works; keep the column references absolute",[266,1487,1489],{"id":1488},"step-4-cover-rows-that-will-be-added-later","Step 4 — Cover rows that will be added later",[10,1491,1492,1493,1496],{},"A rule applied to ",[19,1494,1495],{},"A2:E6"," stops at row 6, so invoices appended next week stay uncoloured. Either apply the rule to a generous range from the start, or — better — convert\nthe data to an Excel table and apply the rule to a range that the next append will fall inside.",[271,1498,1500],{"className":300,"code":1499,"language":302,"meta":276,"style":276},"wb = load_workbook(\"invoices.xlsx\")\nws = wb[\"Invoices\"]\nws.conditional_formatting.add(\"A2:E5000\", FormulaRule(\n    formula=['$D2=\"Overdue\"'], fill=fill(\"FFC7CE\"), font=Font(color=\"9C0006\")))\n\nws.append([\"INV-206\", \"Proseware\", 530, \"Overdue\", \"2026-09-01\"])   # row 7, inside the rule\nwb.save(\"invoices_future.xlsx\")\n",[19,1501,1502,1514,1526,1536,1572,1576,1608],{"__ignoreMap":276},[280,1503,1504,1506,1508,1510,1512],{"class":282,"line":283},[280,1505,333],{"class":313},[280,1507,336],{"class":309},[280,1509,666],{"class":313},[280,1511,590],{"class":290},[280,1513,593],{"class":313},[280,1515,1516,1518,1520,1522,1524],{"class":282,"line":323},[280,1517,345],{"class":313},[280,1519,336],{"class":309},[280,1521,679],{"class":313},[280,1523,682],{"class":290},[280,1525,560],{"class":313},[280,1527,1528,1530,1533],{"class":282,"line":330},[280,1529,788],{"class":313},[280,1531,1532],{"class":290},"\"A2:E5000\"",[280,1534,1535],{"class":313},", FormulaRule(\n",[280,1537,1538,1540,1542,1544,1546,1548,1550,1552,1554,1556,1558,1560,1562,1564,1566,1568,1570],{"class":282,"line":342},[280,1539,714],{"class":713},[280,1541,336],{"class":309},[280,1543,719],{"class":313},[280,1545,722],{"class":290},[280,1547,1316],{"class":313},[280,1549,1156],{"class":713},[280,1551,336],{"class":309},[280,1553,1323],{"class":313},[280,1555,748],{"class":290},[280,1557,1328],{"class":313},[280,1559,1164],{"class":713},[280,1561,336],{"class":309},[280,1563,769],{"class":313},[280,1565,772],{"class":713},[280,1567,336],{"class":309},[280,1569,777],{"class":290},[280,1571,1344],{"class":313},[280,1573,1574],{"class":282,"line":353},[280,1575,327],{"emptyLinePlaceholder":326},[280,1577,1578,1580,1583,1585,1588,1590,1593,1595,1597,1599,1602,1605],{"class":282,"line":364},[280,1579,367],{"class":313},[280,1581,1582],{"class":290},"\"INV-206\"",[280,1584,373],{"class":313},[280,1586,1587],{"class":290},"\"Proseware\"",[280,1589,373],{"class":313},[280,1591,1592],{"class":424},"530",[280,1594,373],{"class":313},[280,1596,430],{"class":290},[280,1598,373],{"class":313},[280,1600,1601],{"class":290},"\"2026-09-01\"",[280,1603,1604],{"class":313},"])   ",[280,1606,1607],{"class":1296},"# row 7, inside the rule\n",[280,1609,1610,1612,1615],{"class":282,"line":397},[280,1611,587],{"class":313},[280,1613,1614],{"class":290},"\"invoices_future.xlsx\"",[280,1616,593],{"class":313},[10,1618,1619,1620,1622,1623,613],{},"A range reaching row 5000 costs nothing measurable: Excel evaluates conditional formats only for cells it draws. Blank rows are unaffected because ",[19,1621,1430],{}," is false for\nan empty cell. Appending in the right place is covered in\n",[28,1624,1626],{"href":1625},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Ffind-the-last-row-with-data-in-openpyxl\u002F","Find the Last Row with Data in openpyxl",[266,1628,1630],{"id":1629},"step-5-do-the-same-with-xlsxwriter","Step 5 — Do the same with xlsxwriter",[10,1632,1633,1634,1637,1638,1640],{},"When the report is generated from scratch, xlsxwriter writes the same rule with ",[19,1635,1636],{},"conditional_format"," and a ",[19,1639,1148],{}," criteria. The formula convention is identical: written for\nthe top-left cell, column absolute, row relative.",[271,1642,1644],{"className":300,"code":1643,"language":302,"meta":276,"style":276},"import xlsxwriter\n\nbook = xlsxwriter.Workbook(\"invoices_xlsxwriter.xlsx\")\nsheet = book.add_worksheet(\"Invoices\")\nsheet.write_row(0, 0, [\"Invoice\", \"Customer\", \"Amount\", \"Status\", \"Due\"])\nfor index, row in enumerate(rows, start=1):\n    sheet.write_row(index, 0, row)\n\noverdue_format = book.add_format({\"bg_color\": \"#FFC7CE\", \"font_color\": \"#9C0006\"})\nsheet.conditional_format(1, 0, len(rows), 4, {\n    \"type\": \"formula\",\n    \"criteria\": '=$D2=\"Overdue\"',\n    \"format\": overdue_format,\n})\nbook.close()\n",[19,1645,1646,1653,1657,1672,1686,1720,1745,1755,1759,1791,1814,1827,1839,1847,1851],{"__ignoreMap":276},[280,1647,1648,1650],{"class":282,"line":283},[280,1649,317],{"class":309},[280,1651,1652],{"class":313}," xlsxwriter\n",[280,1654,1655],{"class":282,"line":323},[280,1656,327],{"emptyLinePlaceholder":326},[280,1658,1659,1662,1664,1667,1670],{"class":282,"line":330},[280,1660,1661],{"class":313},"book ",[280,1663,336],{"class":309},[280,1665,1666],{"class":313}," xlsxwriter.Workbook(",[280,1668,1669],{"class":290},"\"invoices_xlsxwriter.xlsx\"",[280,1671,593],{"class":313},[280,1673,1674,1677,1679,1682,1684],{"class":282,"line":342},[280,1675,1676],{"class":313},"sheet ",[280,1678,336],{"class":309},[280,1680,1681],{"class":313}," book.add_worksheet(",[280,1683,682],{"class":290},[280,1685,593],{"class":313},[280,1687,1688,1691,1693,1695,1697,1700,1702,1704,1706,1708,1710,1712,1714,1716,1718],{"class":282,"line":353},[280,1689,1690],{"class":313},"sheet.write_row(",[280,1692,55],{"class":424},[280,1694,373],{"class":313},[280,1696,55],{"class":424},[280,1698,1699],{"class":313},", [",[280,1701,370],{"class":290},[280,1703,373],{"class":313},[280,1705,376],{"class":290},[280,1707,373],{"class":313},[280,1709,381],{"class":290},[280,1711,373],{"class":313},[280,1713,386],{"class":290},[280,1715,373],{"class":313},[280,1717,391],{"class":290},[280,1719,394],{"class":313},[280,1721,1722,1724,1727,1729,1732,1735,1738,1740,1742],{"class":282,"line":364},[280,1723,566],{"class":309},[280,1725,1726],{"class":313}," index, row ",[280,1728,572],{"class":309},[280,1730,1731],{"class":424}," enumerate",[280,1733,1734],{"class":313},"(rows, ",[280,1736,1737],{"class":713},"start",[280,1739,336],{"class":309},[280,1741,108],{"class":424},[280,1743,1744],{"class":313},"):\n",[280,1746,1747,1750,1752],{"class":282,"line":397},[280,1748,1749],{"class":313},"    sheet.write_row(index, ",[280,1751,55],{"class":424},[280,1753,1754],{"class":313},", row)\n",[280,1756,1757],{"class":282,"line":408},[280,1758,327],{"emptyLinePlaceholder":326},[280,1760,1761,1764,1766,1769,1772,1775,1778,1780,1783,1785,1788],{"class":282,"line":441},[280,1762,1763],{"class":313},"overdue_format ",[280,1765,336],{"class":309},[280,1767,1768],{"class":313}," book.add_format({",[280,1770,1771],{"class":290},"\"bg_color\"",[280,1773,1774],{"class":313},": ",[280,1776,1777],{"class":290},"\"#FFC7CE\"",[280,1779,373],{"class":313},[280,1781,1782],{"class":290},"\"font_color\"",[280,1784,1774],{"class":313},[280,1786,1787],{"class":290},"\"#9C0006\"",[280,1789,1790],{"class":313},"})\n",[280,1792,1793,1796,1798,1800,1802,1804,1806,1809,1811],{"class":282,"line":470},[280,1794,1795],{"class":313},"sheet.conditional_format(",[280,1797,108],{"class":424},[280,1799,373],{"class":313},[280,1801,55],{"class":424},[280,1803,373],{"class":313},[280,1805,1205],{"class":424},[280,1807,1808],{"class":313},"(rows), ",[280,1810,203],{"class":424},[280,1812,1813],{"class":313},", {\n",[280,1815,1816,1819,1821,1824],{"class":282,"line":500},[280,1817,1818],{"class":290},"    \"type\"",[280,1820,1774],{"class":313},[280,1822,1823],{"class":290},"\"formula\"",[280,1825,1826],{"class":313},",\n",[280,1828,1829,1832,1834,1837],{"class":282,"line":528},[280,1830,1831],{"class":290},"    \"criteria\"",[280,1833,1774],{"class":313},[280,1835,1836],{"class":290},"'=$D2=\"Overdue\"'",[280,1838,1826],{"class":313},[280,1840,1841,1844],{"class":282,"line":557},[280,1842,1843],{"class":290},"    \"format\"",[280,1845,1846],{"class":313},": overdue_format,\n",[280,1848,1849],{"class":282,"line":563},[280,1850,1790],{"class":313},[280,1852,1853],{"class":282,"line":578},[280,1854,1855],{"class":313},"book.close()\n",[10,1857,1858,1859,1861,1862,613],{},"xlsxwriter's criteria string includes the leading ",[19,1860,336],{},"; openpyxl's formula list does not. Mixing them up produces a rule that Excel reports as invalid. More xlsxwriter rule types\nare in ",[28,1863,1865],{"href":1864},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fapply-conditional-formatting-with-xlsxwriter\u002F","Apply Conditional Formatting with xlsxwriter",[266,1867,1869],{"id":1868},"step-6-check-the-rules-and-predict-what-will-be-coloured","Step 6 — Check the rules and predict what will be coloured",[10,1871,1872],{},"Conditional formatting cannot be seen without opening Excel, which makes it easy to ship a rule with a typo in a status word. Two checks catch most mistakes before\nanyone else does: read the rules back from the saved file, and evaluate the same conditions in pandas to see how many rows each one should colour.",[271,1874,1876],{"className":300,"code":1875,"language":302,"meta":276,"style":276},"import pandas as pd\n\nsaved = load_workbook(\"invoices_status.xlsx\")[\"Invoices\"]\nfor cf_range in saved.conditional_formatting:\n    for rule in cf_range.rules:\n        print(cf_range.sqref, rule.type, rule.formula, \"stop\" if rule.stopIfTrue else \"\")\n\ndata = pd.read_excel(\"invoices_status.xlsx\", sheet_name=\"Invoices\")\nexpected = {\n    \"Overdue\": data[\"Status\"].str.lower().eq(\"overdue\").sum(),\n    \"Pending\": data[\"Status\"].str.lower().eq(\"pending\").sum(),\n    \"Paid\": data[\"Status\"].str.lower().eq(\"paid\").sum(),\n}\nunmatched = data.loc[~data[\"Status\"].str.lower().isin([\"overdue\", \"pending\", \"paid\"]), \"Status\"]\nprint(expected, \"no rule for:\", unmatched.unique().tolist())\n",[19,1877,1878,1891,1895,1913,1925,1938,1963,1967,1990,2000,2018,2034,2050,2055,2093],{"__ignoreMap":276},[280,1879,1880,1882,1885,1888],{"class":282,"line":283},[280,1881,317],{"class":309},[280,1883,1884],{"class":313}," pandas ",[280,1886,1887],{"class":309},"as",[280,1889,1890],{"class":313}," pd\n",[280,1892,1893],{"class":282,"line":323},[280,1894,327],{"emptyLinePlaceholder":326},[280,1896,1897,1900,1902,1904,1906,1909,1911],{"class":282,"line":330},[280,1898,1899],{"class":313},"saved ",[280,1901,336],{"class":309},[280,1903,666],{"class":313},[280,1905,1191],{"class":290},[280,1907,1908],{"class":313},")[",[280,1910,682],{"class":290},[280,1912,560],{"class":313},[280,1914,1915,1917,1920,1922],{"class":282,"line":342},[280,1916,566],{"class":309},[280,1918,1919],{"class":313}," cf_range ",[280,1921,572],{"class":309},[280,1923,1924],{"class":313}," saved.conditional_formatting:\n",[280,1926,1927,1930,1933,1935],{"class":282,"line":353},[280,1928,1929],{"class":309},"    for",[280,1931,1932],{"class":313}," rule ",[280,1934,572],{"class":309},[280,1936,1937],{"class":313}," cf_range.rules:\n",[280,1939,1940,1943,1946,1949,1952,1955,1958,1961],{"class":282,"line":364},[280,1941,1942],{"class":424},"        print",[280,1944,1945],{"class":313},"(cf_range.sqref, rule.type, rule.formula, ",[280,1947,1948],{"class":290},"\"stop\"",[280,1950,1951],{"class":309}," if",[280,1953,1954],{"class":313}," rule.stopIfTrue ",[280,1956,1957],{"class":309},"else",[280,1959,1960],{"class":290}," \"\"",[280,1962,593],{"class":313},[280,1964,1965],{"class":282,"line":397},[280,1966,327],{"emptyLinePlaceholder":326},[280,1968,1969,1972,1974,1977,1979,1981,1984,1986,1988],{"class":282,"line":408},[280,1970,1971],{"class":313},"data ",[280,1973,336],{"class":309},[280,1975,1976],{"class":313}," pd.read_excel(",[280,1978,1191],{"class":290},[280,1980,373],{"class":313},[280,1982,1983],{"class":713},"sheet_name",[280,1985,336],{"class":309},[280,1987,682],{"class":290},[280,1989,593],{"class":313},[280,1991,1992,1995,1997],{"class":282,"line":441},[280,1993,1994],{"class":313},"expected ",[280,1996,336],{"class":309},[280,1998,1999],{"class":313}," {\n",[280,2001,2002,2005,2008,2010,2013,2015],{"class":282,"line":470},[280,2003,2004],{"class":290},"    \"Overdue\"",[280,2006,2007],{"class":313},": data[",[280,2009,386],{"class":290},[280,2011,2012],{"class":313},"].str.lower().eq(",[280,2014,1220],{"class":290},[280,2016,2017],{"class":313},").sum(),\n",[280,2019,2020,2023,2025,2027,2029,2032],{"class":282,"line":500},[280,2021,2022],{"class":290},"    \"Pending\"",[280,2024,2007],{"class":313},[280,2026,386],{"class":290},[280,2028,2012],{"class":313},[280,2030,2031],{"class":290},"\"pending\"",[280,2033,2017],{"class":313},[280,2035,2036,2039,2041,2043,2045,2048],{"class":282,"line":528},[280,2037,2038],{"class":290},"    \"Paid\"",[280,2040,2007],{"class":313},[280,2042,386],{"class":290},[280,2044,2012],{"class":313},[280,2046,2047],{"class":290},"\"paid\"",[280,2049,2017],{"class":313},[280,2051,2052],{"class":282,"line":557},[280,2053,2054],{"class":313},"}\n",[280,2056,2057,2060,2062,2065,2068,2071,2073,2076,2078,2080,2082,2084,2086,2089,2091],{"class":282,"line":563},[280,2058,2059],{"class":313},"unmatched ",[280,2061,336],{"class":309},[280,2063,2064],{"class":313}," data.loc[",[280,2066,2067],{"class":309},"~",[280,2069,2070],{"class":313},"data[",[280,2072,386],{"class":290},[280,2074,2075],{"class":313},"].str.lower().isin([",[280,2077,1220],{"class":290},[280,2079,373],{"class":313},[280,2081,2031],{"class":290},[280,2083,373],{"class":313},[280,2085,2047],{"class":290},[280,2087,2088],{"class":313},"]), ",[280,2090,386],{"class":290},[280,2092,560],{"class":313},[280,2094,2095,2097,2100,2103],{"class":282,"line":578},[280,2096,1199],{"class":424},[280,2098,2099],{"class":313},"(expected, ",[280,2101,2102],{"class":290},"\"no rule for:\"",[280,2104,2105],{"class":313},", unmatched.unique().tolist())\n",[10,2107,2108,2109,2112,2113,2116,2117,2121],{},"Lower-casing both sides mirrors Excel's case-insensitive comparison, so the counts match what a reader will see. The ",[19,2110,2111],{},"unmatched"," list is the valuable part: a status such as\n",[19,2114,2115],{},"Part-paid"," that no rule covers shows up immediately, instead of being noticed weeks later as the one row that never turns any colour. When statuses come from people typing,\npair the rules with a dropdown as in\n",[28,2118,2120],{"href":2119},"\u002Fadvanced-data-transformation-and-cleaning\u002Fvalidating-excel-data-with-python\u002Fadd-dropdown-data-validation-to-excel-with-openpyxl\u002F","Add Dropdown Data Validation to Excel with openpyxl",",\nso the list of values the rules expect and the list people can choose from are the same list.",[266,2123,2125],{"id":2124},"common-pitfalls","Common pitfalls",[2127,2128,2129,2145],"table",{},[2130,2131,2132],"thead",{},[2133,2134,2135,2139,2142],"tr",{},[2136,2137,2138],"th",{},"Symptom",[2136,2140,2141],{},"Cause",[2136,2143,2144],{},"Fix",[2146,2147,2148,2163,2174,2190,2203,2220],"tbody",{},[2133,2149,2150,2154,2157],{},[2151,2152,2153],"td",{},"Only one column is coloured",[2151,2155,2156],{},"Rule range covers only the Status column",[2151,2158,2159,2160],{},"Apply the rule to the full row range, e.g. ",[19,2161,2162],{},"A2:E500",[2133,2164,2165,2168,2171],{},[2151,2166,2167],{},"Colours land on the wrong rows",[2151,2169,2170],{},"Formula written for row 1 but range starts at row 2",[2151,2172,2173],{},"Write the formula for the range's first row",[2133,2175,2176,2179,2185],{},[2151,2177,2178],{},"Patchy colouring across columns",[2151,2180,2181,2182,2184],{},"Column reference not absolute (",[19,2183,864],{},")",[2151,2186,2187,2188],{},"Use ",[19,2189,612],{},[2133,2191,2192,2195,2200],{},[2151,2193,2194],{},"Every row copies the first row's colour",[2151,2196,2197,2198,2184],{},"Row made absolute (",[19,2199,888],{},[2151,2201,2202],{},"Remove the dollar sign before the row",[2133,2204,2205,2208,2214],{},[2151,2206,2207],{},"Excel says the file has a problem with a rule",[2151,2209,2210,2211,2213],{},"Leading ",[19,2212,336],{}," in an openpyxl formula, or smart quotes",[2151,2215,2216,2217,2219],{},"No ",[19,2218,336],{}," in openpyxl; use straight quotes",[2133,2221,2222,2225,2228],{},[2151,2223,2224],{},"New rows are not coloured",[2151,2226,2227],{},"Rule range ends at the last current row",[2151,2229,2230],{},"Extend the range generously",[266,2232,2234],{"id":2233},"performance-and-scale-notes","Performance and scale notes",[10,2236,2237,2238,2240,2241,2244],{},"A formula rule is stored once for its whole range, so file size does not grow with the number of rows it covers, and writing it is instant. Excel evaluates the formula for visible\ncells as it draws them, which is cheap for simple comparisons. Very large ranges with volatile functions such as ",[19,2239,1231],{}," or ",[19,2242,2243],{},"INDIRECT"," in the rule can slow scrolling on big sheets;\nkeep rule formulas simple, and prefer a helper column computed in Python when the condition is complex.",[266,2246,2248],{"id":2247},"conclusion","Conclusion",[10,2250,2251,2252,2254,2255,2257,2258,2260,2261,1236,2263,2265,2266,2268],{},"Highlight whole rows with a ",[19,2253,603],{}," applied across the full width of the table and written for its first row, using a column-absolute, row-relative reference such as ",[19,2256,612],{},".\nAdd one rule per colour, use ",[19,2259,937],{}," where conditions overlap, combine conditions with ",[19,2262,1235],{},[19,2264,1239],{},", and extend the range beyond the current data so appended rows are covered.\nThe same rule works in xlsxwriter with a leading ",[19,2267,336],{}," in the criteria. Because the rule is evaluated by Excel, the colours keep following the data after people edit, sort and add rows.",[266,2270,2272],{"id":2271},"frequently-asked-questions","Frequently asked questions",[10,2274,2275,2278],{},[606,2276,2277],{},"Why does my rule colour only the Status cell?","\nThe rule's range decides which cells can be coloured. Apply it to every column of the row, such as A2:E500, while the formula still tests column D.",[10,2280,2281,2284],{},[606,2282,2283],{},"How do I highlight rows that contain a word anywhere in a column?","\nUse a formula such as ISNUMBER(SEARCH(\"urgent\", $B2)), which is true when the text appears anywhere in that cell, ignoring case.",[10,2286,2287,2290],{},[606,2288,2289],{},"Can I highlight rows based on a value in another sheet?","\nYes. Modern Excel accepts references to other sheets in conditional formatting formulas, or you can define a named range on the other sheet and refer to the name.",[10,2292,2293,2296],{},[606,2294,2295],{},"Will sorting the data break the colours?","\nNo. The rule is evaluated per row from the current values, so after sorting each row is coloured according to its own Status cell.",[266,2298,2300],{"id":2299},"related","Related",[2302,2303,2304,2311,2318,2325,2332],"ul",{},[2305,2306,2307,2308,2310],"li",{},"Up one level: ",[28,2309,31],{"href":30}," — every rule type openpyxl can write.",[2305,2312,2313,2317],{},[28,2314,2316],{"href":2315},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fopenpyxl-apply-conditional-formatting-to-range\u002F","Apply Conditional Formatting to a Range in openpyxl"," — ranges and rule basics.",[2305,2319,2320,2324],{},[28,2321,2323],{"href":2322},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fhighlight-cells-above-a-threshold-with-openpyxl\u002F","Highlight Cells Above a Threshold with openpyxl"," — single-cell rules.",[2305,2326,2327,2331],{},[28,2328,2330],{"href":2329},"\u002Fadvanced-data-transformation-and-cleaning\u002Fvalidating-excel-data-with-python\u002Fhighlight-invalid-cells-in-excel-with-python\u002F","Highlight Invalid Cells in Excel with Python"," — colouring validation failures.",[2305,2333,2334,2338],{},[28,2335,2337],{"href":2336},"\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fread-cell-colours-and-font-styles-as-data-with-openpyxl\u002F","Read Cell Colours and Font Styles as Data with openpyxl"," — why rule colours are invisible to cell.fill.",[2340,2341,2342],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}",{"title":276,"searchDepth":323,"depth":323,"links":2344},[2345,2346,2347,2348,2349,2350,2351,2352,2353,2354,2355,2356],{"id":268,"depth":323,"text":269},{"id":596,"depth":323,"text":597},{"id":930,"depth":323,"text":931},{"id":1224,"depth":323,"text":1225},{"id":1488,"depth":323,"text":1489},{"id":1629,"depth":323,"text":1630},{"id":1868,"depth":323,"text":1869},{"id":2124,"depth":323,"text":2125},{"id":2233,"depth":323,"text":2234},{"id":2247,"depth":323,"text":2248},{"id":2271,"depth":323,"text":2272},{"id":2299,"depth":323,"text":2300},"2026-09-17","Colour a whole row when its Status is Overdue or its amount crosses a limit, using a formula-based conditional format with the right mix of absolute and relative references — so the colour follows the data after edits and sorts.","md",[2361,2363,2365,2367],{"q":2277,"a":2362},"The rule's range decides which cells can be coloured. Apply it to every column of the row, such as A2:E500, while the formula still tests column D.",{"q":2283,"a":2364},"Use a formula such as ISNUMBER(SEARCH(\"urgent\", $B2)), which is true when the text appears anywhere in that cell, ignoring case.",{"q":2289,"a":2366},"Yes. Modern Excel accepts references to other sheets in conditional formatting formulas, or you can define a named range on the other sheet and refer to the name.",{"q":2295,"a":2368},"No. The rule is evaluated per row from the current values, so after sorting each row is coloured according to its own Status cell.","highlight an entire row based on a cell value with openpyxl",null,{"breadcrumb":2372},[2373,2376,2379],{"name":2374,"item":2375},"Home","\u002F",{"name":2377,"item":2378},"Advanced Data Transformation and Cleaning","\u002Fadvanced-data-transformation-and-cleaning\u002F",{"name":31,"item":30},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fhighlight-an-entire-row-based-on-a-cell-value-with-openpyxl",{"title":5,"description":2382},"Use openpyxl FormulaRule to highlight whole rows in Excel from one column's value: $D2 references, text and numeric conditions, several rules with priority and stopIfTrue, and xlsxwriter's equivalent.","highlight-an-entire-row-based-on-a-cell-value-with-openpyxl","advanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fhighlight-an-entire-row-based-on-a-cell-value-with-openpyxl\u002Findex",[2386,2387],"conditional-formatting","openpyxl","how-to","ZeJD7zzf6QZJQLazlitd2RAGqhLSPVXjhbZDGpflsCw",[2391,2395],{"title":2392,"path":2393,"stem":2394,"children":-1},"Control Conditional Formatting Rule Order and Stop If True","\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fcontrol-conditional-formatting-rule-order-and-stop-if-true","advanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fcontrol-conditional-formatting-rule-order-and-stop-if-true\u002Findex",{"title":2396,"path":2397,"stem":2398,"children":-1},"Highlight Blank Cells in Excel with openpyxl","\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fhighlight-blank-cells-with-openpyxl","advanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fhighlight-blank-cells-with-openpyxl\u002Findex",[2400,2402],{"path":2401,"title":31},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl",{"path":2403,"title":2404},"\u002Fadvanced-data-transformation-and-cleaning","Advanced Data Transformation and Cleaning for Excel Automation",[],[2407,2409,2412,2415,2418,2421],{"path":2408,"title":1865,"kind":2388},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fapply-conditional-formatting-with-xlsxwriter",{"path":2410,"title":2411,"kind":2388},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fadd-data-bars-and-colour-scales-with-openpyxl","Add Data Bars and Colour Scales with openpyxl",{"path":2413,"title":2414,"kind":2388},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fadd-icon-sets-with-openpyxl","Add Icon Sets to Excel Cells with openpyxl",{"path":2416,"title":2417,"kind":2388},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fapply-a-colour-scale-per-row-or-per-column-with-openpyxl","Apply a Colour Scale per Row or per Column with openpyxl",{"path":2419,"title":2420,"kind":2388},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fapply-conditional-formatting-to-an-excel-table-that-grows","Apply Conditional Formatting to an Excel Table That Grows",{"path":2393,"title":2392,"kind":2388},1790972078132]