[{"data":1,"prerenderedAt":2662},["ShallowReactive",2],{"doc:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-excel-tables-and-autofilters-with-python\u002Fchoose-and-customise-excel-table-styles-with-python":3,"surround:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-excel-tables-and-autofilters-with-python\u002Fchoose-and-customise-excel-table-styles-with-python":2629,"ancestors:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-excel-tables-and-autofilters-with-python\u002Fchoose-and-customise-excel-table-styles-with-python":2637,"children:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-excel-tables-and-autofilters-with-python\u002Fchoose-and-customise-excel-table-styles-with-python":2642,"related:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-excel-tables-and-autofilters-with-python\u002Fchoose-and-customise-excel-table-styles-with-python":2643},{"id":4,"title":5,"body":6,"dateModified":2597,"datePublished":2597,"description":2598,"extension":2599,"faq":2600,"keyword":2609,"kind":2610,"meta":2611,"navigation":125,"path":2619,"seo":2620,"slug":2622,"stem":2623,"topics":2624,"type":2627,"__hash__":2628},"docs\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-excel-tables-and-autofilters-with-python\u002Fchoose-and-customise-excel-table-styles-with-python\u002Findex.md","Choose and Customise Excel Table Styles with Python",{"type":7,"value":8,"toc":2584},"minimark",[9,24,29,61,72,76,83,224,243,373,377,384,615,630,634,637,903,916,920,935,1449,1475,1607,1611,1618,1849,1852,1856,1863,2096,2105,2399,2406,2462,2466,2504,2508,2522,2526,2532,2538,2544,2550,2554,2581],[10,11,12,13,18,19,23],"p",{},"An Excel table brings filter buttons, structured references and a growing range — and a style that colours the header, stripes the rows and can bold the first and last columns. From Python the style is a single name plus four switches, which makes it easy to pick one, easy to mistype one, and easy to miss that some applications ignore table styles altogether. This page goes through the built-in style families and the switches, sets them from openpyxl and XlsxWriter, writes a custom company table style, adds a check for style names, and shows how to make a table look right when the workbook is opened in LibreOffice or converted to PDF. Creating the table itself is covered in ",[14,15,17],"a",{"href":16},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-excel-tables-and-autofilters-with-python\u002Fcreate-an-excel-table-with-python\u002F","Create an Excel Table with Python","; this page is about how it looks. It belongs to ",[14,20,22],{"href":21},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-excel-tables-and-autofilters-with-python\u002F","Creating Excel Tables and Autofilters with Python",".",[25,26,28],"h2",{"id":27},"prerequisites","Prerequisites",[30,31,36],"pre",{"className":32,"code":33,"language":34,"meta":35,"style":35},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install openpyxl xlsxwriter pillow\n","bash","",[37,38,39],"code",{"__ignoreMap":35},[40,41,44,48,52,55,58],"span",{"class":42,"line":43},"line",1,[40,45,47],{"class":46},"sMTad","pip",[40,49,51],{"class":50},"srMev"," install",[40,53,54],{"class":50}," openpyxl",[40,56,57],{"class":50}," xlsxwriter",[40,59,60],{"class":50}," pillow\n",[10,62,63,64,67,68,71],{},"The examples use a small regional sales table with a header row and four data rows (",[37,65,66],{},"rows"," in the code). Step 6 uses LibreOffice (",[37,69,70],{},"soffice",") and Pillow to render the result and look at its colours; that part is optional.",[25,73,75],{"id":74},"step-1-how-the-built-in-styles-are-named","Step 1 — How the built-in styles are named",[10,77,78,79,82],{},"Excel ships 60 table styles in three families, and every one has an internal name of the form ",[37,80,81],{},"TableStyle\u003CFamily>\u003CNumber>",":",[30,84,88],{"className":85,"code":86,"language":87,"meta":35,"style":35},"language-python shiki shiki-themes github-light github-dark-high-contrast","from collections import Counter\nfrom openpyxl.worksheet.table import TABLESTYLES\n\nfamilies = Counter(name.rstrip(\"0123456789\").replace(\"TableStyle\", \"\") for name in TABLESTYLES)\nprint(dict(families))\nprint(TABLESTYLES[0], \"…\", TABLESTYLES[-1])\n","python",[37,89,90,106,120,127,172,187],{"__ignoreMap":35},[40,91,92,96,100,103],{"class":42,"line":43},[40,93,95],{"class":94},"s-kum","from",[40,97,99],{"class":98},"skGVy"," collections ",[40,101,102],{"class":94},"import",[40,104,105],{"class":98}," Counter\n",[40,107,109,111,114,116],{"class":42,"line":108},2,[40,110,95],{"class":94},[40,112,113],{"class":98}," openpyxl.worksheet.table ",[40,115,102],{"class":94},[40,117,119],{"class":118},"sP0c6"," TABLESTYLES\n",[40,121,123],{"class":42,"line":122},3,[40,124,126],{"emptyLinePlaceholder":125},true,"\n",[40,128,130,133,136,139,142,145,148,151,154,157,160,163,166,169],{"class":42,"line":129},4,[40,131,132],{"class":98},"families ",[40,134,135],{"class":94},"=",[40,137,138],{"class":98}," Counter(name.rstrip(",[40,140,141],{"class":50},"\"0123456789\"",[40,143,144],{"class":98},").replace(",[40,146,147],{"class":50},"\"TableStyle\"",[40,149,150],{"class":98},", ",[40,152,153],{"class":50},"\"\"",[40,155,156],{"class":98},") ",[40,158,159],{"class":94},"for",[40,161,162],{"class":98}," name ",[40,164,165],{"class":94},"in",[40,167,168],{"class":118}," TABLESTYLES",[40,170,171],{"class":98},")\n",[40,173,175,178,181,184],{"class":42,"line":174},5,[40,176,177],{"class":118},"print",[40,179,180],{"class":98},"(",[40,182,183],{"class":118},"dict",[40,185,186],{"class":98},"(families))\n",[40,188,190,192,194,197,200,203,206,209,211,213,215,218,221],{"class":42,"line":189},6,[40,191,177],{"class":118},[40,193,180],{"class":98},[40,195,196],{"class":118},"TABLESTYLES",[40,198,199],{"class":98},"[",[40,201,202],{"class":118},"0",[40,204,205],{"class":98},"], ",[40,207,208],{"class":50},"\"…\"",[40,210,150],{"class":98},[40,212,196],{"class":118},[40,214,199],{"class":98},[40,216,217],{"class":94},"-",[40,219,220],{"class":118},"1",[40,222,223],{"class":98},"])\n",[10,225,226,227,230,231,235,236,239,240,242],{},"There are 21 Light styles, 28 Medium and 11 Dark. Within each family the numbers run in groups of seven colours in the order of the default Office theme: the first style of each group is neutral (black or grey), and the next six use the theme's accent colours (the last four Dark styles break the pattern with two-colour combinations). ",[37,228,229],{},"TableStyleMedium9"," — the blue banded style Excel picks by default — is the second style of the second Medium group. Because the colours come from the workbook's theme, the same style name looks different in a workbook with a different theme. Excel shows the styles in a gallery under ",[232,233,234],"em",{},"Table Design"," → ",[232,237,238],{},"Table Styles",", which is the quickest way to choose one: hover to preview, then note the name from its tooltip (\"Medium 9\" means ",[37,241,229],{},").",[244,245,253,254,253,258,253,262,253,269,253,278,253,289,253,296,253,302,253,307,253,311,253,315,253,320,253,326,253,331,253,335,253,338,253,341,253,344,253,349,253,355,253,360,253,364,253,367,253,370],"svg",{"viewBox":246,"role":247,"ariaLabelledBy":248,"xmlns":251,"style":252},"4 0 752 207","img",[249,250],"ts-families-t","ts-families-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[255,256,257],"title",{"id":249},"The three families of built-in table styles",[259,260,261],"desc",{"id":250},"Light styles have thin lines and pale or no fill, Medium styles have a coloured header and banded rows, and Dark styles have a dark body with strong contrast. Each family cycles through a neutral style and six theme accent colours.",[263,264],"rect",{"x":265,"y":202,"width":266,"height":267,"fill":268},"4","752","207","#ffffff",[263,270],{"x":271,"y":272,"width":273,"height":274,"rx":275,"fill":268,"stroke":276,"style":277},"20.0","16.0","228.0","175.0","14","var(--line,#cdd5e6)","stroke-width:2px",[279,280,282],"g",{"transform":281},"translate(121.0,29.0) scale(1.083)",[283,284],"path",{"d":285,"fill":286,"stroke":287,"style":288},"M3 5h18v14H3z M3 10h18 M3 15h18 M9 5v14","none","var(--text,#172033)","stroke-width:1.66px;stroke-linecap:round;stroke-linejoin:round",[290,291,295],"text",{"x":292,"y":293,"style":294},"134.0","78.0","font-size:14px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Light 1–21",[42,297],{"x1":298,"y1":299,"x2":300,"y2":299,"stroke":276,"style":301},"36.0","91.0","232.0","stroke-width:1px",[290,303,306],{"x":292,"y":304,"style":305},"111.0","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","thin rules, little fill",[290,308,310],{"x":292,"y":309,"style":305},"137.0","prints cleanly in mono",[290,312,314],{"x":292,"y":313,"style":305},"163.0","good for dense numbers",[263,316],{"x":317,"y":272,"width":273,"height":274,"rx":275,"fill":318,"stroke":319,"style":277},"266.0","#f0f4ff","var(--brand,#5b5cf0)",[279,321,323],{"transform":322},"translate(367.0,29.0) scale(1.083)",[283,324],{"d":285,"fill":286,"stroke":325,"style":288},"var(--brand-strong,#4338ca)",[290,327,330],{"x":328,"y":293,"style":329},"380.0","font-size:14px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Medium 1–28",[42,332],{"x1":333,"y1":299,"x2":334,"y2":299,"stroke":319,"style":301},"282.0","478.0",[290,336,337],{"x":328,"y":304,"style":305},"coloured header row",[290,339,340],{"x":328,"y":309,"style":305},"banded body rows",[290,342,343],{"x":328,"y":313,"style":305},"Medium9 is Excel's default",[263,345],{"x":346,"y":272,"width":273,"height":274,"rx":275,"fill":347,"stroke":348,"style":277},"512.0","#fdefd8","var(--gold,#b4740a)",[279,350,352],{"transform":351},"translate(613.0,29.0) scale(1.083)",[283,353],{"d":285,"fill":286,"stroke":354,"style":288},"var(--gold-ink,#7a4e06)",[290,356,359],{"x":357,"y":293,"style":358},"626.0","font-size:14px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Dark 1–11",[42,361],{"x1":362,"y1":299,"x2":363,"y2":299,"stroke":348,"style":301},"528.0","724.0",[290,365,366],{"x":357,"y":304,"style":305},"dark, saturated fill",[290,368,369],{"x":357,"y":309,"style":305},"high contrast headline",[290,371,372],{"x":357,"y":313,"style":305},"sparing use in reports",[25,374,376],{"id":375},"step-2-the-four-switches","Step 2 — The four switches",[10,378,379,380,383],{},"A style defines formats for several ",[232,381,382],{},"table elements"," — header row, total row, first and last column, row stripes, column stripes — and four switches decide which of them apply:",[30,385,387],{"className":85,"code":386,"language":87,"meta":35,"style":35},"from openpyxl import Workbook\nfrom openpyxl.worksheet.table import Table, TableStyleInfo\n\nwb = Workbook()\nws = wb.active\nws.title = \"Sales\"\nfor r in rows:\n    ws.append(r)\nws.add_table(Table(displayName=\"Sales\", ref=\"A1:D5\", tableStyleInfo=TableStyleInfo(\n    name=\"TableStyleMedium2\",\n    showRowStripes=True,       # alternate row shading\n    showColumnStripes=False,   # alternate column shading\n    showFirstColumn=True,      # emphasise the label column\n    showLastColumn=True,       # emphasise the total column\n)))\nwb.save(\"table_switches.xlsx\")\nprint(ws.tables[\"Sales\"].tableStyleInfo.name, \"first\u002Flast column on\")\n",[37,388,389,401,412,416,426,436,446,459,465,500,514,532,549,565,580,586,597],{"__ignoreMap":35},[40,390,391,393,396,398],{"class":42,"line":43},[40,392,95],{"class":94},[40,394,395],{"class":98}," openpyxl ",[40,397,102],{"class":94},[40,399,400],{"class":98}," Workbook\n",[40,402,403,405,407,409],{"class":42,"line":108},[40,404,95],{"class":94},[40,406,113],{"class":98},[40,408,102],{"class":94},[40,410,411],{"class":98}," Table, TableStyleInfo\n",[40,413,414],{"class":42,"line":122},[40,415,126],{"emptyLinePlaceholder":125},[40,417,418,421,423],{"class":42,"line":129},[40,419,420],{"class":98},"wb ",[40,422,135],{"class":94},[40,424,425],{"class":98}," Workbook()\n",[40,427,428,431,433],{"class":42,"line":174},[40,429,430],{"class":98},"ws ",[40,432,135],{"class":94},[40,434,435],{"class":98}," wb.active\n",[40,437,438,441,443],{"class":42,"line":189},[40,439,440],{"class":98},"ws.title ",[40,442,135],{"class":94},[40,444,445],{"class":50}," \"Sales\"\n",[40,447,449,451,454,456],{"class":42,"line":448},7,[40,450,159],{"class":94},[40,452,453],{"class":98}," r ",[40,455,165],{"class":94},[40,457,458],{"class":98}," rows:\n",[40,460,462],{"class":42,"line":461},8,[40,463,464],{"class":98},"    ws.append(r)\n",[40,466,468,471,475,477,480,482,485,487,490,492,495,497],{"class":42,"line":467},9,[40,469,470],{"class":98},"ws.add_table(Table(",[40,472,474],{"class":473},"sa561","displayName",[40,476,135],{"class":94},[40,478,479],{"class":50},"\"Sales\"",[40,481,150],{"class":98},[40,483,484],{"class":473},"ref",[40,486,135],{"class":94},[40,488,489],{"class":50},"\"A1:D5\"",[40,491,150],{"class":98},[40,493,494],{"class":473},"tableStyleInfo",[40,496,135],{"class":94},[40,498,499],{"class":98},"TableStyleInfo(\n",[40,501,503,506,508,511],{"class":42,"line":502},10,[40,504,505],{"class":473},"    name",[40,507,135],{"class":94},[40,509,510],{"class":50},"\"TableStyleMedium2\"",[40,512,513],{"class":98},",\n",[40,515,517,520,522,525,528],{"class":42,"line":516},11,[40,518,519],{"class":473},"    showRowStripes",[40,521,135],{"class":94},[40,523,524],{"class":118},"True",[40,526,527],{"class":98},",       ",[40,529,531],{"class":530},"s-wDw","# alternate row shading\n",[40,533,535,538,540,543,546],{"class":42,"line":534},12,[40,536,537],{"class":473},"    showColumnStripes",[40,539,135],{"class":94},[40,541,542],{"class":118},"False",[40,544,545],{"class":98},",   ",[40,547,548],{"class":530},"# alternate column shading\n",[40,550,552,555,557,559,562],{"class":42,"line":551},13,[40,553,554],{"class":473},"    showFirstColumn",[40,556,135],{"class":94},[40,558,524],{"class":118},[40,560,561],{"class":98},",      ",[40,563,564],{"class":530},"# emphasise the label column\n",[40,566,568,571,573,575,577],{"class":42,"line":567},14,[40,569,570],{"class":473},"    showLastColumn",[40,572,135],{"class":94},[40,574,524],{"class":118},[40,576,527],{"class":98},[40,578,579],{"class":530},"# emphasise the total column\n",[40,581,583],{"class":42,"line":582},15,[40,584,585],{"class":98},")))\n",[40,587,589,592,595],{"class":42,"line":588},16,[40,590,591],{"class":98},"wb.save(",[40,593,594],{"class":50},"\"table_switches.xlsx\"",[40,596,171],{"class":98},[40,598,600,602,605,607,610,613],{"class":42,"line":599},17,[40,601,177],{"class":118},[40,603,604],{"class":98},"(ws.tables[",[40,606,479],{"class":50},[40,608,609],{"class":98},"].tableStyleInfo.name, ",[40,611,612],{"class":50},"\"first\u002Flast column on\"",[40,614,171],{"class":98},[10,616,617,618,621,622,625,626,23],{},"For a report with labels in the first column and a total in the last, turning on first and last column emphasis makes both stand out; for a wide table of similar measures, column stripes help the eye down each column. Row stripes are the usual default. The header row itself is part of the table definition (",[37,619,620],{},"headerRowCount","), not a switch, and the total row is controlled by ",[37,623,624],{},"totalsRowCount"," — adding one is covered in ",[14,627,629],{"href":628},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-excel-tables-and-autofilters-with-python\u002Fadd-a-total-row-to-an-excel-table\u002F","Add a Total Row to an Excel Table",[25,631,633],{"id":632},"step-3-the-same-in-xlsxwriter","Step 3 — The same in XlsxWriter",[10,635,636],{},"XlsxWriter writes the same table XML but uses Excel's display names and lower-case switch names:",[30,638,640],{"className":85,"code":639,"language":87,"meta":35,"style":35},"import xlsxwriter\n\nbook = xlsxwriter.Workbook(\"table_xlsxwriter.xlsx\")\nsheet = book.add_worksheet(\"Sales\")\nsheet.add_table(\"A1:D5\", {\n    \"name\": \"Sales\",\n    \"style\": \"Table Style Light 11\",\n    \"banded_rows\": False,\n    \"banded_columns\": True,\n    \"first_column\": True,\n    \"last_column\": True,\n    \"columns\": [{\"header\": h} for h in rows[0]],\n    \"data\": rows[1:],\n})\nplain = book.add_worksheet(\"Plain\")\nplain.add_table(\"A1:D5\", {\"name\": \"PlainSales\", \"style\": None,\n                          \"columns\": [{\"header\": h} for h in rows[0]], \"data\": rows[1:]})\nbook.close()\nprint(\"written with Table Style Light 11 and with no style\")\n",[37,641,642,649,653,668,682,692,704,716,727,738,749,760,789,802,807,821,851,885,891],{"__ignoreMap":35},[40,643,644,646],{"class":42,"line":43},[40,645,102],{"class":94},[40,647,648],{"class":98}," xlsxwriter\n",[40,650,651],{"class":42,"line":108},[40,652,126],{"emptyLinePlaceholder":125},[40,654,655,658,660,663,666],{"class":42,"line":122},[40,656,657],{"class":98},"book ",[40,659,135],{"class":94},[40,661,662],{"class":98}," xlsxwriter.Workbook(",[40,664,665],{"class":50},"\"table_xlsxwriter.xlsx\"",[40,667,171],{"class":98},[40,669,670,673,675,678,680],{"class":42,"line":129},[40,671,672],{"class":98},"sheet ",[40,674,135],{"class":94},[40,676,677],{"class":98}," book.add_worksheet(",[40,679,479],{"class":50},[40,681,171],{"class":98},[40,683,684,687,689],{"class":42,"line":174},[40,685,686],{"class":98},"sheet.add_table(",[40,688,489],{"class":50},[40,690,691],{"class":98},", {\n",[40,693,694,697,700,702],{"class":42,"line":189},[40,695,696],{"class":50},"    \"name\"",[40,698,699],{"class":98},": ",[40,701,479],{"class":50},[40,703,513],{"class":98},[40,705,706,709,711,714],{"class":42,"line":448},[40,707,708],{"class":50},"    \"style\"",[40,710,699],{"class":98},[40,712,713],{"class":50},"\"Table Style Light 11\"",[40,715,513],{"class":98},[40,717,718,721,723,725],{"class":42,"line":461},[40,719,720],{"class":50},"    \"banded_rows\"",[40,722,699],{"class":98},[40,724,542],{"class":118},[40,726,513],{"class":98},[40,728,729,732,734,736],{"class":42,"line":467},[40,730,731],{"class":50},"    \"banded_columns\"",[40,733,699],{"class":98},[40,735,524],{"class":118},[40,737,513],{"class":98},[40,739,740,743,745,747],{"class":42,"line":502},[40,741,742],{"class":50},"    \"first_column\"",[40,744,699],{"class":98},[40,746,524],{"class":118},[40,748,513],{"class":98},[40,750,751,754,756,758],{"class":42,"line":516},[40,752,753],{"class":50},"    \"last_column\"",[40,755,699],{"class":98},[40,757,524],{"class":118},[40,759,513],{"class":98},[40,761,762,765,768,771,774,776,779,781,784,786],{"class":42,"line":534},[40,763,764],{"class":50},"    \"columns\"",[40,766,767],{"class":98},": [{",[40,769,770],{"class":50},"\"header\"",[40,772,773],{"class":98},": h} ",[40,775,159],{"class":94},[40,777,778],{"class":98}," h ",[40,780,165],{"class":94},[40,782,783],{"class":98}," rows[",[40,785,202],{"class":118},[40,787,788],{"class":98},"]],\n",[40,790,791,794,797,799],{"class":42,"line":551},[40,792,793],{"class":50},"    \"data\"",[40,795,796],{"class":98},": rows[",[40,798,220],{"class":118},[40,800,801],{"class":98},":],\n",[40,803,804],{"class":42,"line":567},[40,805,806],{"class":98},"})\n",[40,808,809,812,814,816,819],{"class":42,"line":582},[40,810,811],{"class":98},"plain ",[40,813,135],{"class":94},[40,815,677],{"class":98},[40,817,818],{"class":50},"\"Plain\"",[40,820,171],{"class":98},[40,822,823,826,828,831,834,836,839,841,844,846,849],{"class":42,"line":588},[40,824,825],{"class":98},"plain.add_table(",[40,827,489],{"class":50},[40,829,830],{"class":98},", {",[40,832,833],{"class":50},"\"name\"",[40,835,699],{"class":98},[40,837,838],{"class":50},"\"PlainSales\"",[40,840,150],{"class":98},[40,842,843],{"class":50},"\"style\"",[40,845,699],{"class":98},[40,847,848],{"class":118},"None",[40,850,513],{"class":98},[40,852,853,856,858,860,862,864,866,868,870,872,875,878,880,882],{"class":42,"line":599},[40,854,855],{"class":50},"                          \"columns\"",[40,857,767],{"class":98},[40,859,770],{"class":50},[40,861,773],{"class":98},[40,863,159],{"class":94},[40,865,778],{"class":98},[40,867,165],{"class":94},[40,869,783],{"class":98},[40,871,202],{"class":118},[40,873,874],{"class":98},"]], ",[40,876,877],{"class":50},"\"data\"",[40,879,796],{"class":98},[40,881,220],{"class":118},[40,883,884],{"class":98},":]})\n",[40,886,888],{"class":42,"line":887},18,[40,889,890],{"class":98},"book.close()\n",[40,892,894,896,898,901],{"class":42,"line":893},19,[40,895,177],{"class":118},[40,897,180],{"class":98},[40,899,900],{"class":50},"\"written with Table Style Light 11 and with no style\"",[40,902,171],{"class":98},[10,904,905,907,908,911,912,915],{},[37,906,713],{}," is written to the file as ",[37,909,910],{},"TableStyleLight11",". ",[37,913,914],{},"\"style\": None"," gives a table with filter buttons and structured references but no colours at all — useful when you want to apply your own cell formatting. XlsxWriter has no API for custom table styles; for those, use openpyxl as in the next step.",[25,917,919],{"id":918},"step-4-a-custom-company-table-style","Step 4 — A custom company table style",[10,921,922,923,926,927,930,931,934],{},"A custom table style is a named set of ",[232,924,925],{},"differential formats"," (the same partial formats conditional formatting uses), one per table element. openpyxl can write one, though only through private attributes of the workbook — ",[37,928,929],{},"_differential_styles"," and ",[37,932,933],{},"_table_styles"," — so pin your openpyxl version and keep this code in one function:",[30,936,938],{"className":85,"code":937,"language":87,"meta":35,"style":35},"from openpyxl.styles import Border, Font, PatternFill, Side\nfrom openpyxl.styles.differential import DifferentialStyle\nfrom openpyxl.styles.table import TableStyle, TableStyleElement\n\ndef add_table_style(wb, name, elements):\n    \"\"\"elements: {\"headerRow\": DifferentialStyle, ...}; returns the style name.\"\"\"\n    parts = []\n    for kind, dxf in elements.items():\n        wb._differential_styles.append(dxf)\n        parts.append(TableStyleElement(type=kind, dxfId=len(wb._differential_styles.dxf) - 1))\n    wb._table_styles.tableStyle.append(TableStyle(name=name, pivot=False, count=len(parts), tableStyleElement=parts))\n    return name\n\nwb = Workbook()\nws = wb.active\nws.title = \"Sales\"\nfor r in rows:\n    ws.append(r)\nbrand = add_table_style(wb, \"BrandTable\", {\n    \"wholeTable\": DifferentialStyle(border=Border(bottom=Side(\"thin\", color=\"C7D2FE\"))),\n    \"headerRow\": DifferentialStyle(font=Font(bold=True, color=\"FFFFFF\"), fill=PatternFill(\"solid\", bgColor=\"4338CA\")),\n    \"firstRowStripe\": DifferentialStyle(fill=PatternFill(\"solid\", bgColor=\"EEF2FF\")),\n    \"lastColumn\": DifferentialStyle(font=Font(bold=True)),\n})\nws.add_table(Table(displayName=\"Sales\", ref=\"A1:D5\",\n                   tableStyleInfo=TableStyleInfo(name=brand, showRowStripes=True, showLastColumn=True)))\nwb.save(\"table_custom.xlsx\")\n\nfrom openpyxl import load_workbook\nback = load_workbook(\"table_custom.xlsx\")\nprint([s.name for s in back._table_styles.tableStyle], \"→\", back[\"Sales\"].tables[\"Sales\"].tableStyleInfo.name)\n",[37,939,940,952,964,976,980,992,997,1007,1020,1025,1057,1097,1105,1109,1117,1125,1133,1143,1147,1162,1203,1262,1289,1311,1316,1337,1373,1383,1388,1400,1415],{"__ignoreMap":35},[40,941,942,944,947,949],{"class":42,"line":43},[40,943,95],{"class":94},[40,945,946],{"class":98}," openpyxl.styles ",[40,948,102],{"class":94},[40,950,951],{"class":98}," Border, Font, PatternFill, Side\n",[40,953,954,956,959,961],{"class":42,"line":108},[40,955,95],{"class":94},[40,957,958],{"class":98}," openpyxl.styles.differential ",[40,960,102],{"class":94},[40,962,963],{"class":98}," DifferentialStyle\n",[40,965,966,968,971,973],{"class":42,"line":122},[40,967,95],{"class":94},[40,969,970],{"class":98}," openpyxl.styles.table ",[40,972,102],{"class":94},[40,974,975],{"class":98}," TableStyle, TableStyleElement\n",[40,977,978],{"class":42,"line":129},[40,979,126],{"emptyLinePlaceholder":125},[40,981,982,985,989],{"class":42,"line":174},[40,983,984],{"class":94},"def",[40,986,988],{"class":987},"s_Opv"," add_table_style",[40,990,991],{"class":98},"(wb, name, elements):\n",[40,993,994],{"class":42,"line":189},[40,995,996],{"class":50},"    \"\"\"elements: {\"headerRow\": DifferentialStyle, ...}; returns the style name.\"\"\"\n",[40,998,999,1002,1004],{"class":42,"line":448},[40,1000,1001],{"class":98},"    parts ",[40,1003,135],{"class":94},[40,1005,1006],{"class":98}," []\n",[40,1008,1009,1012,1015,1017],{"class":42,"line":461},[40,1010,1011],{"class":94},"    for",[40,1013,1014],{"class":98}," kind, dxf ",[40,1016,165],{"class":94},[40,1018,1019],{"class":98}," elements.items():\n",[40,1021,1022],{"class":42,"line":467},[40,1023,1024],{"class":98},"        wb._differential_styles.append(dxf)\n",[40,1026,1027,1030,1033,1035,1038,1041,1043,1046,1049,1051,1054],{"class":42,"line":502},[40,1028,1029],{"class":98},"        parts.append(TableStyleElement(",[40,1031,1032],{"class":473},"type",[40,1034,135],{"class":94},[40,1036,1037],{"class":98},"kind, ",[40,1039,1040],{"class":473},"dxfId",[40,1042,135],{"class":94},[40,1044,1045],{"class":118},"len",[40,1047,1048],{"class":98},"(wb._differential_styles.dxf) ",[40,1050,217],{"class":94},[40,1052,1053],{"class":118}," 1",[40,1055,1056],{"class":98},"))\n",[40,1058,1059,1062,1065,1067,1070,1073,1075,1077,1079,1082,1084,1086,1089,1092,1094],{"class":42,"line":516},[40,1060,1061],{"class":98},"    wb._table_styles.tableStyle.append(TableStyle(",[40,1063,1064],{"class":473},"name",[40,1066,135],{"class":94},[40,1068,1069],{"class":98},"name, ",[40,1071,1072],{"class":473},"pivot",[40,1074,135],{"class":94},[40,1076,542],{"class":118},[40,1078,150],{"class":98},[40,1080,1081],{"class":473},"count",[40,1083,135],{"class":94},[40,1085,1045],{"class":118},[40,1087,1088],{"class":98},"(parts), ",[40,1090,1091],{"class":473},"tableStyleElement",[40,1093,135],{"class":94},[40,1095,1096],{"class":98},"parts))\n",[40,1098,1099,1102],{"class":42,"line":534},[40,1100,1101],{"class":94},"    return",[40,1103,1104],{"class":98}," name\n",[40,1106,1107],{"class":42,"line":551},[40,1108,126],{"emptyLinePlaceholder":125},[40,1110,1111,1113,1115],{"class":42,"line":567},[40,1112,420],{"class":98},[40,1114,135],{"class":94},[40,1116,425],{"class":98},[40,1118,1119,1121,1123],{"class":42,"line":582},[40,1120,430],{"class":98},[40,1122,135],{"class":94},[40,1124,435],{"class":98},[40,1126,1127,1129,1131],{"class":42,"line":588},[40,1128,440],{"class":98},[40,1130,135],{"class":94},[40,1132,445],{"class":50},[40,1134,1135,1137,1139,1141],{"class":42,"line":599},[40,1136,159],{"class":94},[40,1138,453],{"class":98},[40,1140,165],{"class":94},[40,1142,458],{"class":98},[40,1144,1145],{"class":42,"line":887},[40,1146,464],{"class":98},[40,1148,1149,1152,1154,1157,1160],{"class":42,"line":893},[40,1150,1151],{"class":98},"brand ",[40,1153,135],{"class":94},[40,1155,1156],{"class":98}," add_table_style(wb, ",[40,1158,1159],{"class":50},"\"BrandTable\"",[40,1161,691],{"class":98},[40,1163,1165,1168,1171,1174,1176,1179,1182,1184,1187,1190,1192,1195,1197,1200],{"class":42,"line":1164},20,[40,1166,1167],{"class":50},"    \"wholeTable\"",[40,1169,1170],{"class":98},": DifferentialStyle(",[40,1172,1173],{"class":473},"border",[40,1175,135],{"class":94},[40,1177,1178],{"class":98},"Border(",[40,1180,1181],{"class":473},"bottom",[40,1183,135],{"class":94},[40,1185,1186],{"class":98},"Side(",[40,1188,1189],{"class":50},"\"thin\"",[40,1191,150],{"class":98},[40,1193,1194],{"class":473},"color",[40,1196,135],{"class":94},[40,1198,1199],{"class":50},"\"C7D2FE\"",[40,1201,1202],{"class":98},"))),\n",[40,1204,1206,1209,1211,1214,1216,1219,1222,1224,1226,1228,1230,1232,1235,1238,1241,1243,1246,1249,1251,1254,1256,1259],{"class":42,"line":1205},21,[40,1207,1208],{"class":50},"    \"headerRow\"",[40,1210,1170],{"class":98},[40,1212,1213],{"class":473},"font",[40,1215,135],{"class":94},[40,1217,1218],{"class":98},"Font(",[40,1220,1221],{"class":473},"bold",[40,1223,135],{"class":94},[40,1225,524],{"class":118},[40,1227,150],{"class":98},[40,1229,1194],{"class":473},[40,1231,135],{"class":94},[40,1233,1234],{"class":50},"\"FFFFFF\"",[40,1236,1237],{"class":98},"), ",[40,1239,1240],{"class":473},"fill",[40,1242,135],{"class":94},[40,1244,1245],{"class":98},"PatternFill(",[40,1247,1248],{"class":50},"\"solid\"",[40,1250,150],{"class":98},[40,1252,1253],{"class":473},"bgColor",[40,1255,135],{"class":94},[40,1257,1258],{"class":50},"\"4338CA\"",[40,1260,1261],{"class":98},")),\n",[40,1263,1265,1268,1270,1272,1274,1276,1278,1280,1282,1284,1287],{"class":42,"line":1264},22,[40,1266,1267],{"class":50},"    \"firstRowStripe\"",[40,1269,1170],{"class":98},[40,1271,1240],{"class":473},[40,1273,135],{"class":94},[40,1275,1245],{"class":98},[40,1277,1248],{"class":50},[40,1279,150],{"class":98},[40,1281,1253],{"class":473},[40,1283,135],{"class":94},[40,1285,1286],{"class":50},"\"EEF2FF\"",[40,1288,1261],{"class":98},[40,1290,1292,1295,1297,1299,1301,1303,1305,1307,1309],{"class":42,"line":1291},23,[40,1293,1294],{"class":50},"    \"lastColumn\"",[40,1296,1170],{"class":98},[40,1298,1213],{"class":473},[40,1300,135],{"class":94},[40,1302,1218],{"class":98},[40,1304,1221],{"class":473},[40,1306,135],{"class":94},[40,1308,524],{"class":118},[40,1310,1261],{"class":98},[40,1312,1314],{"class":42,"line":1313},24,[40,1315,806],{"class":98},[40,1317,1319,1321,1323,1325,1327,1329,1331,1333,1335],{"class":42,"line":1318},25,[40,1320,470],{"class":98},[40,1322,474],{"class":473},[40,1324,135],{"class":94},[40,1326,479],{"class":50},[40,1328,150],{"class":98},[40,1330,484],{"class":473},[40,1332,135],{"class":94},[40,1334,489],{"class":50},[40,1336,513],{"class":98},[40,1338,1340,1343,1345,1348,1350,1352,1355,1358,1360,1362,1364,1367,1369,1371],{"class":42,"line":1339},26,[40,1341,1342],{"class":473},"                   tableStyleInfo",[40,1344,135],{"class":94},[40,1346,1347],{"class":98},"TableStyleInfo(",[40,1349,1064],{"class":473},[40,1351,135],{"class":94},[40,1353,1354],{"class":98},"brand, ",[40,1356,1357],{"class":473},"showRowStripes",[40,1359,135],{"class":94},[40,1361,524],{"class":118},[40,1363,150],{"class":98},[40,1365,1366],{"class":473},"showLastColumn",[40,1368,135],{"class":94},[40,1370,524],{"class":118},[40,1372,585],{"class":98},[40,1374,1376,1378,1381],{"class":42,"line":1375},27,[40,1377,591],{"class":98},[40,1379,1380],{"class":50},"\"table_custom.xlsx\"",[40,1382,171],{"class":98},[40,1384,1386],{"class":42,"line":1385},28,[40,1387,126],{"emptyLinePlaceholder":125},[40,1389,1391,1393,1395,1397],{"class":42,"line":1390},29,[40,1392,95],{"class":94},[40,1394,395],{"class":98},[40,1396,102],{"class":94},[40,1398,1399],{"class":98}," load_workbook\n",[40,1401,1403,1406,1408,1411,1413],{"class":42,"line":1402},30,[40,1404,1405],{"class":98},"back ",[40,1407,135],{"class":94},[40,1409,1410],{"class":98}," load_workbook(",[40,1412,1380],{"class":50},[40,1414,171],{"class":98},[40,1416,1418,1420,1423,1425,1428,1430,1433,1436,1439,1441,1444,1446],{"class":42,"line":1417},31,[40,1419,177],{"class":118},[40,1421,1422],{"class":98},"([s.name ",[40,1424,159],{"class":94},[40,1426,1427],{"class":98}," s ",[40,1429,165],{"class":94},[40,1431,1432],{"class":98}," back._table_styles.tableStyle], ",[40,1434,1435],{"class":50},"\"→\"",[40,1437,1438],{"class":98},", back[",[40,1440,479],{"class":50},[40,1442,1443],{"class":98},"].tables[",[40,1445,479],{"class":50},[40,1447,1448],{"class":98},"].tableStyleInfo.name)\n",[10,1450,1451,1452,1455,1456,1459,1460,1463,1464,1466,1467,1470,1471,23],{},"The workbook's ",[37,1453,1454],{},"styles.xml"," now holds a ",[37,1457,1458],{},"BrandTable"," style with four elements, and the table refers to it by name; reloading the file shows the style survives a round trip through openpyxl. In Excel it appears under ",[232,1461,1462],{},"Custom"," at the top of the table style gallery, so colleagues can apply it to their own tables. Note that in differential formats a solid fill's colour goes in ",[37,1465,1253],{},", not ",[37,1468,1469],{},"fgColor"," — the same rule as for conditional formatting in ",[14,1472,1474],{"href":1473},"\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",[244,1476,253,1481,253,1484,253,1487,253,1490,253,1495,253,1502,253,1508,253,1512,253,1517,253,1521,253,1526,253,1531,253,1536,253,1543,253,1549,253,1553,253,1557,253,1561,253,1564,253,1567,253,1572,253,1577,253,1580,253,1584,253,1587,253,1592,253,1599,253,1604],{"viewBox":1477,"role":247,"ariaLabelledBy":1478,"xmlns":251,"style":252},"4 0 752 160",[1479,1480],"ts-custom-t","ts-custom-d",[255,1482,1483],{"id":1479},"How a custom table style is put together",[259,1485,1486],{"id":1480},"Each table element such as the header row or the first row stripe gets a differential format. The formats are stored in the workbook's dxf list, a table style lists the elements with their dxf ids, and the table refers to the style by name.",[263,1488],{"x":265,"y":202,"width":266,"height":1489,"fill":268},"160",[263,1491],{"x":271,"y":272,"width":1492,"height":1493,"rx":1494,"fill":318,"stroke":319,"style":277},"153.0","128.0","12",[279,1496,1498],{"transform":1497},"translate(84.5,29.0) scale(1.000)",[283,1499],{"d":1500,"fill":286,"stroke":325,"style":1501},"M12 3l9 5-9 5-9-5z M3 13l9 5 9-5 M3 17l9 5 9-5","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[290,1503,1507],{"x":1504,"y":1505,"style":1506},"96.5","75.5","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Differential",[290,1509,1511],{"x":1504,"y":1510,"style":1506},"93.0","formats",[290,1513,1516],{"x":1504,"y":1514,"style":1515},"112.5","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","font, fill, border per",[290,1518,1520],{"x":1504,"y":1519,"style":1515},"130.0","element",[42,1522],{"x1":1523,"y1":1524,"x2":1525,"y2":1524,"stroke":319,"style":277},"178.0","80.0","200.0",[1527,1528],"polygon",{"points":1529,"fill":1530},"204.0,80.0 195.0,85.0 195.0,75.0","#5b5cf0",[263,1532],{"x":1533,"y":272,"width":1492,"height":1493,"rx":1494,"fill":1534,"stroke":1535,"style":277},"209.0","#d9f4f1","var(--teal,#0f9488)",[279,1537,1539],{"transform":1538},"translate(273.5,37.8) scale(1.000)",[283,1540],{"d":1541,"fill":286,"stroke":1542,"style":1501},"M12 9a3 3 0 1 0 .01 0z M12 2.5v3 M12 18.5v3 M2.5 12h3 M18.5 12h3 M5.3 5.3l2.1 2.1 M16.6 16.6l2.1 2.1 M5.3 18.7l2.1-2.1 M16.6 7.4l2.1-2.1","var(--teal-ink,#0b6157)",[290,1544,1548],{"x":1545,"y":1546,"style":1547},"285.5","84.2","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Table style",[290,1550,1552],{"x":1545,"y":1551,"style":1515},"103.8","BrandTable: element",[290,1554,1556],{"x":1545,"y":1555,"style":1515},"121.2","→ dxf id",[42,1558],{"x1":1559,"y1":1524,"x2":1560,"y2":1524,"stroke":319,"style":277},"367.0","389.0",[1527,1562],{"points":1563,"fill":1530},"393.0,80.0 384.0,85.0 384.0,75.0",[263,1565],{"x":1566,"y":272,"width":1492,"height":1493,"rx":1494,"fill":347,"stroke":348,"style":277},"398.0",[279,1568,1570],{"transform":1569},"translate(462.5,46.5) scale(1.000)",[283,1571],{"d":285,"fill":286,"stroke":354,"style":1501},[290,1573,1576],{"x":1574,"y":1510,"style":1575},"474.5","font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","The table",[290,1578,1579],{"x":1574,"y":1514,"style":1515},"tableStyleInfo name",[42,1581],{"x1":1582,"y1":1524,"x2":1583,"y2":1524,"stroke":319,"style":277},"556.0","578.0",[1527,1585],{"points":1586,"fill":1530},"582.0,80.0 573.0,85.0 573.0,75.0",[263,1588],{"x":1589,"y":272,"width":1492,"height":1493,"rx":1494,"fill":1590,"stroke":1591,"style":277},"587.0","#fee8f2","var(--accent,#f43f8f)",[279,1593,1595],{"transform":1594},"translate(651.5,46.5) scale(1.000)",[283,1596],{"d":1597,"fill":286,"stroke":1598,"style":1501},"M2 12s4-7 10-7 10 7 10 7-4 7-10 7S2 12 2 12z M12 9a3 3 0 1 0 .01 0z","var(--accent-ink,#be185d)",[290,1600,1603],{"x":1601,"y":1510,"style":1602},"663.5","font-size:13.5px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","Excel gallery",[290,1605,1606],{"x":1601,"y":1514,"style":1515},"listed under Custom",[25,1608,1610],{"id":1609},"step-5-catch-misspelt-style-names","Step 5 — Catch misspelt style names",[10,1612,1613,1614,1617],{},"openpyxl and Excel accept any string as a style name. A typo such as ",[37,1615,1616],{},"TableStyleMedium29"," (there are only 28) is written without complaint, and the table then appears unstyled. A small check before saving catches it:",[30,1619,1621],{"className":85,"code":1620,"language":87,"meta":35,"style":35},"def check_table_styles(wb):\n    known = set(TABLESTYLES) | {s.name for s in wb._table_styles.tableStyle}\n    problems = []\n    for sheet in wb.worksheets:\n        for table in sheet.tables.values():\n            name = table.tableStyleInfo.name if table.tableStyleInfo else None\n            if name is not None and name not in known:\n                problems.append(f\"{sheet.title}!{table.displayName}: unknown style {name!r}\")\n    return problems\n\nws.tables[\"Sales\"].tableStyleInfo.name = \"TableStyleMedium29\"\nprint(check_table_styles(wb))\nws.tables[\"Sales\"].tableStyleInfo.name = brand\nprint(check_table_styles(wb))\n",[37,1622,1623,1633,1664,1673,1685,1698,1720,1750,1797,1804,1808,1823,1830,1843],{"__ignoreMap":35},[40,1624,1625,1627,1630],{"class":42,"line":43},[40,1626,984],{"class":94},[40,1628,1629],{"class":987}," check_table_styles",[40,1631,1632],{"class":98},"(wb):\n",[40,1634,1635,1638,1640,1643,1645,1647,1649,1652,1655,1657,1659,1661],{"class":42,"line":108},[40,1636,1637],{"class":98},"    known ",[40,1639,135],{"class":94},[40,1641,1642],{"class":118}," set",[40,1644,180],{"class":98},[40,1646,196],{"class":118},[40,1648,156],{"class":98},[40,1650,1651],{"class":94},"|",[40,1653,1654],{"class":98}," {s.name ",[40,1656,159],{"class":94},[40,1658,1427],{"class":98},[40,1660,165],{"class":94},[40,1662,1663],{"class":98}," wb._table_styles.tableStyle}\n",[40,1665,1666,1669,1671],{"class":42,"line":122},[40,1667,1668],{"class":98},"    problems ",[40,1670,135],{"class":94},[40,1672,1006],{"class":98},[40,1674,1675,1677,1680,1682],{"class":42,"line":129},[40,1676,1011],{"class":94},[40,1678,1679],{"class":98}," sheet ",[40,1681,165],{"class":94},[40,1683,1684],{"class":98}," wb.worksheets:\n",[40,1686,1687,1690,1693,1695],{"class":42,"line":174},[40,1688,1689],{"class":94},"        for",[40,1691,1692],{"class":98}," table ",[40,1694,165],{"class":94},[40,1696,1697],{"class":98}," sheet.tables.values():\n",[40,1699,1700,1703,1705,1708,1711,1714,1717],{"class":42,"line":189},[40,1701,1702],{"class":98},"            name ",[40,1704,135],{"class":94},[40,1706,1707],{"class":98}," table.tableStyleInfo.name ",[40,1709,1710],{"class":94},"if",[40,1712,1713],{"class":98}," table.tableStyleInfo ",[40,1715,1716],{"class":94},"else",[40,1718,1719],{"class":118}," None\n",[40,1721,1722,1725,1727,1730,1733,1736,1739,1741,1744,1747],{"class":42,"line":448},[40,1723,1724],{"class":94},"            if",[40,1726,162],{"class":98},[40,1728,1729],{"class":94},"is",[40,1731,1732],{"class":94}," not",[40,1734,1735],{"class":118}," None",[40,1737,1738],{"class":94}," and",[40,1740,162],{"class":98},[40,1742,1743],{"class":94},"not",[40,1745,1746],{"class":94}," in",[40,1748,1749],{"class":98}," known:\n",[40,1751,1752,1755,1758,1761,1765,1768,1771,1774,1776,1779,1781,1784,1786,1788,1791,1793,1795],{"class":42,"line":461},[40,1753,1754],{"class":98},"                problems.append(",[40,1756,1757],{"class":94},"f",[40,1759,1760],{"class":50},"\"",[40,1762,1764],{"class":1763},"sSjpA","{",[40,1766,1767],{"class":98},"sheet.title",[40,1769,1770],{"class":1763},"}",[40,1772,1773],{"class":50},"!",[40,1775,1764],{"class":1763},[40,1777,1778],{"class":98},"table.displayName",[40,1780,1770],{"class":1763},[40,1782,1783],{"class":50},": unknown style ",[40,1785,1764],{"class":1763},[40,1787,1064],{"class":98},[40,1789,1790],{"class":94},"!r",[40,1792,1770],{"class":1763},[40,1794,1760],{"class":50},[40,1796,171],{"class":98},[40,1798,1799,1801],{"class":42,"line":467},[40,1800,1101],{"class":94},[40,1802,1803],{"class":98}," problems\n",[40,1805,1806],{"class":42,"line":502},[40,1807,126],{"emptyLinePlaceholder":125},[40,1809,1810,1813,1815,1818,1820],{"class":42,"line":516},[40,1811,1812],{"class":98},"ws.tables[",[40,1814,479],{"class":50},[40,1816,1817],{"class":98},"].tableStyleInfo.name ",[40,1819,135],{"class":94},[40,1821,1822],{"class":50}," \"TableStyleMedium29\"\n",[40,1824,1825,1827],{"class":42,"line":534},[40,1826,177],{"class":118},[40,1828,1829],{"class":98},"(check_table_styles(wb))\n",[40,1831,1832,1834,1836,1838,1840],{"class":42,"line":551},[40,1833,1812],{"class":98},[40,1835,479],{"class":50},[40,1837,1817],{"class":98},[40,1839,135],{"class":94},[40,1841,1842],{"class":98}," brand\n",[40,1844,1845,1847],{"class":42,"line":567},[40,1846,177],{"class":118},[40,1848,1829],{"class":98},[10,1850,1851],{},"The check accepts the 60 built-in names plus any custom styles defined in the workbook, and reports the sheet and table of anything else. Running it in a test of the report generator stops a silently plain table reaching readers.",[25,1853,1855],{"id":1854},"step-6-tables-in-libreoffice-and-pdf-exports","Step 6 — Tables in LibreOffice and PDF exports",[10,1857,1858,1859,1862],{},"Table styles are drawn by Excel. LibreOffice (tested with version 26.2) keeps the table's range and filter buttons but does ",[1860,1861,1743],"strong",{}," draw table styles — neither built-in ones like Medium9 nor custom ones — so the same file opened there, or converted to PDF with LibreOffice on a server, shows a plain grid. If that matters, add the look as ordinary cell formatting on top of the table. Excel lets direct cell formatting take precedence over the table style, and LibreOffice draws it as normal:",[30,1864,1866],{"className":85,"code":1865,"language":87,"meta":35,"style":35},"from openpyxl.styles import Font, PatternFill\n\ndef bake_table_look(ws, table, header=\"4338CA\", stripe=\"EEF2FF\"):\n    \"\"\"Copy a simple header + row-stripe look into direct cell formatting.\"\"\"\n    (first, *body) = ws[table.ref]\n    for cell in first:\n        cell.font = Font(bold=True, color=\"FFFFFF\")\n        cell.fill = PatternFill(\"solid\", fgColor=header)\n    for i, row in enumerate(body):\n        if i % 2 == 0:\n            for cell in row:\n                cell.fill = PatternFill(\"solid\", fgColor=stripe)\n\nbake_table_look(ws, ws.tables[\"Sales\"])\nwb.save(\"table_baked.xlsx\")\nprint(\"header and stripes copied into\", ws.tables[\"Sales\"].ref)\n",[37,1867,1868,1879,1883,1907,1912,1928,1940,1966,1987,2002,2025,2037,2057,2061,2070,2079],{"__ignoreMap":35},[40,1869,1870,1872,1874,1876],{"class":42,"line":43},[40,1871,95],{"class":94},[40,1873,946],{"class":98},[40,1875,102],{"class":94},[40,1877,1878],{"class":98}," Font, PatternFill\n",[40,1880,1881],{"class":42,"line":108},[40,1882,126],{"emptyLinePlaceholder":125},[40,1884,1885,1887,1890,1893,1895,1897,1900,1902,1904],{"class":42,"line":122},[40,1886,984],{"class":94},[40,1888,1889],{"class":987}," bake_table_look",[40,1891,1892],{"class":98},"(ws, table, header",[40,1894,135],{"class":94},[40,1896,1258],{"class":50},[40,1898,1899],{"class":98},", stripe",[40,1901,135],{"class":94},[40,1903,1286],{"class":50},[40,1905,1906],{"class":98},"):\n",[40,1908,1909],{"class":42,"line":129},[40,1910,1911],{"class":50},"    \"\"\"Copy a simple header + row-stripe look into direct cell formatting.\"\"\"\n",[40,1913,1914,1917,1920,1923,1925],{"class":42,"line":174},[40,1915,1916],{"class":98},"    (first, ",[40,1918,1919],{"class":94},"*",[40,1921,1922],{"class":98},"body) ",[40,1924,135],{"class":94},[40,1926,1927],{"class":98}," ws[table.ref]\n",[40,1929,1930,1932,1935,1937],{"class":42,"line":189},[40,1931,1011],{"class":94},[40,1933,1934],{"class":98}," cell ",[40,1936,165],{"class":94},[40,1938,1939],{"class":98}," first:\n",[40,1941,1942,1945,1947,1950,1952,1954,1956,1958,1960,1962,1964],{"class":42,"line":448},[40,1943,1944],{"class":98},"        cell.font ",[40,1946,135],{"class":94},[40,1948,1949],{"class":98}," Font(",[40,1951,1221],{"class":473},[40,1953,135],{"class":94},[40,1955,524],{"class":118},[40,1957,150],{"class":98},[40,1959,1194],{"class":473},[40,1961,135],{"class":94},[40,1963,1234],{"class":50},[40,1965,171],{"class":98},[40,1967,1968,1971,1973,1976,1978,1980,1982,1984],{"class":42,"line":461},[40,1969,1970],{"class":98},"        cell.fill ",[40,1972,135],{"class":94},[40,1974,1975],{"class":98}," PatternFill(",[40,1977,1248],{"class":50},[40,1979,150],{"class":98},[40,1981,1469],{"class":473},[40,1983,135],{"class":94},[40,1985,1986],{"class":98},"header)\n",[40,1988,1989,1991,1994,1996,1999],{"class":42,"line":467},[40,1990,1011],{"class":94},[40,1992,1993],{"class":98}," i, row ",[40,1995,165],{"class":94},[40,1997,1998],{"class":118}," enumerate",[40,2000,2001],{"class":98},"(body):\n",[40,2003,2004,2007,2010,2013,2016,2019,2022],{"class":42,"line":502},[40,2005,2006],{"class":94},"        if",[40,2008,2009],{"class":98}," i ",[40,2011,2012],{"class":94},"%",[40,2014,2015],{"class":118}," 2",[40,2017,2018],{"class":94}," ==",[40,2020,2021],{"class":118}," 0",[40,2023,2024],{"class":98},":\n",[40,2026,2027,2030,2032,2034],{"class":42,"line":516},[40,2028,2029],{"class":94},"            for",[40,2031,1934],{"class":98},[40,2033,165],{"class":94},[40,2035,2036],{"class":98}," row:\n",[40,2038,2039,2042,2044,2046,2048,2050,2052,2054],{"class":42,"line":534},[40,2040,2041],{"class":98},"                cell.fill ",[40,2043,135],{"class":94},[40,2045,1975],{"class":98},[40,2047,1248],{"class":50},[40,2049,150],{"class":98},[40,2051,1469],{"class":473},[40,2053,135],{"class":94},[40,2055,2056],{"class":98},"stripe)\n",[40,2058,2059],{"class":42,"line":551},[40,2060,126],{"emptyLinePlaceholder":125},[40,2062,2063,2066,2068],{"class":42,"line":567},[40,2064,2065],{"class":98},"bake_table_look(ws, ws.tables[",[40,2067,479],{"class":50},[40,2069,223],{"class":98},[40,2071,2072,2074,2077],{"class":42,"line":582},[40,2073,591],{"class":98},[40,2075,2076],{"class":50},"\"table_baked.xlsx\"",[40,2078,171],{"class":98},[40,2080,2081,2083,2085,2088,2091,2093],{"class":42,"line":588},[40,2082,177],{"class":118},[40,2084,180],{"class":98},[40,2086,2087],{"class":50},"\"header and stripes copied into\"",[40,2089,2090],{"class":98},", ws.tables[",[40,2092,479],{"class":50},[40,2094,2095],{"class":98},"].ref)\n",[10,2097,2098,2099,2101,2102,2104],{},"Note that ordinary cell fills use ",[37,2100,1469],{}," — it is only differential formats that use ",[37,2103,1253],{},". Rendering both files shows the difference:",[30,2106,2108],{"className":85,"code":2107,"language":87,"meta":35,"style":35},"import subprocess\nfrom PIL import Image\n\ndef colour_share(xlsx, rgb):\n    subprocess.run([\"soffice\", \"--headless\", \"--convert-to\", \"png\", xlsx], check=True, capture_output=True)\n    img = Image.open(xlsx.replace(\".xlsx\", \".png\")).convert(\"RGB\")\n    counts = img.getcolors(img.width * img.height)\n    near = sum(n for n, p in counts if all(abs(a - b) \u003C 12 for a, b in zip(p, rgb)))\n    return near \u002F (img.width * img.height)\n\nindigo = (0x43, 0x38, 0xCA)\nfor f in (\"table_custom.xlsx\", \"table_baked.xlsx\"):\n    print(f\"{f:18} indigo header pixels: {colour_share(f, indigo):.2%}\")\n",[37,2109,2110,2117,2130,2134,2144,2188,2214,2229,2290,2307,2311,2343,2362],{"__ignoreMap":35},[40,2111,2112,2114],{"class":42,"line":43},[40,2113,102],{"class":94},[40,2115,2116],{"class":98}," subprocess\n",[40,2118,2119,2121,2124,2127],{"class":42,"line":108},[40,2120,95],{"class":94},[40,2122,2123],{"class":118}," PIL",[40,2125,2126],{"class":94}," import",[40,2128,2129],{"class":98}," Image\n",[40,2131,2132],{"class":42,"line":122},[40,2133,126],{"emptyLinePlaceholder":125},[40,2135,2136,2138,2141],{"class":42,"line":129},[40,2137,984],{"class":94},[40,2139,2140],{"class":987}," colour_share",[40,2142,2143],{"class":98},"(xlsx, rgb):\n",[40,2145,2146,2149,2152,2154,2157,2159,2162,2164,2167,2170,2173,2175,2177,2179,2182,2184,2186],{"class":42,"line":174},[40,2147,2148],{"class":98},"    subprocess.run([",[40,2150,2151],{"class":50},"\"soffice\"",[40,2153,150],{"class":98},[40,2155,2156],{"class":50},"\"--headless\"",[40,2158,150],{"class":98},[40,2160,2161],{"class":50},"\"--convert-to\"",[40,2163,150],{"class":98},[40,2165,2166],{"class":50},"\"png\"",[40,2168,2169],{"class":98},", xlsx], ",[40,2171,2172],{"class":473},"check",[40,2174,135],{"class":94},[40,2176,524],{"class":118},[40,2178,150],{"class":98},[40,2180,2181],{"class":473},"capture_output",[40,2183,135],{"class":94},[40,2185,524],{"class":118},[40,2187,171],{"class":98},[40,2189,2190,2193,2195,2198,2201,2203,2206,2209,2212],{"class":42,"line":189},[40,2191,2192],{"class":98},"    img ",[40,2194,135],{"class":94},[40,2196,2197],{"class":98}," Image.open(xlsx.replace(",[40,2199,2200],{"class":50},"\".xlsx\"",[40,2202,150],{"class":98},[40,2204,2205],{"class":50},"\".png\"",[40,2207,2208],{"class":98},")).convert(",[40,2210,2211],{"class":50},"\"RGB\"",[40,2213,171],{"class":98},[40,2215,2216,2219,2221,2224,2226],{"class":42,"line":448},[40,2217,2218],{"class":98},"    counts ",[40,2220,135],{"class":94},[40,2222,2223],{"class":98}," img.getcolors(img.width ",[40,2225,1919],{"class":94},[40,2227,2228],{"class":98}," img.height)\n",[40,2230,2231,2234,2236,2239,2242,2244,2247,2249,2252,2254,2257,2259,2262,2265,2267,2270,2273,2276,2279,2282,2284,2287],{"class":42,"line":461},[40,2232,2233],{"class":98},"    near ",[40,2235,135],{"class":94},[40,2237,2238],{"class":118}," sum",[40,2240,2241],{"class":98},"(n ",[40,2243,159],{"class":94},[40,2245,2246],{"class":98}," n, p ",[40,2248,165],{"class":94},[40,2250,2251],{"class":98}," counts ",[40,2253,1710],{"class":94},[40,2255,2256],{"class":118}," all",[40,2258,180],{"class":98},[40,2260,2261],{"class":118},"abs",[40,2263,2264],{"class":98},"(a ",[40,2266,217],{"class":94},[40,2268,2269],{"class":98}," b) ",[40,2271,2272],{"class":94},"\u003C",[40,2274,2275],{"class":118}," 12",[40,2277,2278],{"class":94}," for",[40,2280,2281],{"class":98}," a, b ",[40,2283,165],{"class":94},[40,2285,2286],{"class":118}," zip",[40,2288,2289],{"class":98},"(p, rgb)))\n",[40,2291,2292,2294,2297,2300,2303,2305],{"class":42,"line":467},[40,2293,1101],{"class":94},[40,2295,2296],{"class":98}," near ",[40,2298,2299],{"class":94},"\u002F",[40,2301,2302],{"class":98}," (img.width ",[40,2304,1919],{"class":94},[40,2306,2228],{"class":98},[40,2308,2309],{"class":42,"line":502},[40,2310,126],{"emptyLinePlaceholder":125},[40,2312,2313,2316,2318,2321,2324,2327,2329,2331,2334,2336,2338,2341],{"class":42,"line":516},[40,2314,2315],{"class":98},"indigo ",[40,2317,135],{"class":94},[40,2319,2320],{"class":98}," (",[40,2322,2323],{"class":94},"0x",[40,2325,2326],{"class":118},"43",[40,2328,150],{"class":98},[40,2330,2323],{"class":94},[40,2332,2333],{"class":118},"38",[40,2335,150],{"class":98},[40,2337,2323],{"class":94},[40,2339,2340],{"class":118},"CA",[40,2342,171],{"class":98},[40,2344,2345,2347,2350,2352,2354,2356,2358,2360],{"class":42,"line":534},[40,2346,159],{"class":94},[40,2348,2349],{"class":98}," f ",[40,2351,165],{"class":94},[40,2353,2320],{"class":98},[40,2355,1380],{"class":50},[40,2357,150],{"class":98},[40,2359,2076],{"class":50},[40,2361,1906],{"class":98},[40,2363,2364,2367,2369,2371,2373,2375,2377,2380,2382,2385,2387,2390,2393,2395,2397],{"class":42,"line":551},[40,2365,2366],{"class":118},"    print",[40,2368,180],{"class":98},[40,2370,1757],{"class":94},[40,2372,1760],{"class":50},[40,2374,1764],{"class":1763},[40,2376,1757],{"class":98},[40,2378,2379],{"class":94},":18",[40,2381,1770],{"class":1763},[40,2383,2384],{"class":50}," indigo header pixels: ",[40,2386,1764],{"class":1763},[40,2388,2389],{"class":98},"colour_share(f, indigo)",[40,2391,2392],{"class":94},":.2%",[40,2394,1770],{"class":1763},[40,2396,1760],{"class":50},[40,2398,171],{"class":98},[10,2400,2401,2402,23],{},"The custom-style file renders with no indigo at all in LibreOffice, while the baked file shows the indigo header. The baked formatting is fixed to the rows it was written to — it does not move when rows are sorted or added in Excel the way a table style does — so use it for finished reports and rely on the table style for workbooks people will keep editing. Striping a plain range without a table is covered in ",[14,2403,2405],{"href":2404},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl\u002Fadd-alternating-row-colors-with-openpyxl\u002F","Add Alternating Row Colours with openpyxl",[244,2407,253,2413,253,2416,253,2419,253,2423,253,2429,253,2435,253,2442,253,2447,253,2450,253,2454,253,2457],{"viewBox":2408,"role":247,"ariaLabelledBy":2409,"xmlns":251,"style":2412},"4 0 702 100",[2410,2411],"ts-render-t","ts-render-d","width:100%;max-width:702px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[255,2414,2415],{"id":2410},"Indigo header pixels in the LibreOffice rendering",[259,2417,2418],{"id":2411},"Share of the rendered page in the header colour. The table with the custom style shows none, because LibreOffice does not draw table styles. The table with the look baked into cell formatting shows the indigo header.",[263,2420],{"x":265,"y":202,"width":2421,"height":2422,"fill":268},"702","100",[263,2424],{"x":271,"y":272,"width":2425,"height":2426,"rx":2427,"fill":2428,"stroke":276,"style":301},"164.3","28.0","7","#e7ebef",[290,2430,2434],{"x":2431,"y":2432,"style":2433},"102.2","34.5","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","custom table style",[263,2436],{"x":2437,"y":272,"width":2438,"height":2426,"rx":2427,"fill":2439,"stroke":2440,"style":2441},"194.3","40.3","#fce9e9","var(--danger,#dc2626)","stroke-width:1.5px",[290,2443,2446],{"x":2444,"y":2432,"style":2445},"242.6","font-size:12px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:start","0% · style not drawn",[263,2448],{"x":271,"y":2449,"width":2425,"height":2426,"rx":2427,"fill":2428,"stroke":276,"style":301},"56.0",[290,2451,2453],{"x":2431,"y":2452,"style":2433},"74.5","baked cell formatting",[263,2455],{"x":2437,"y":2449,"width":2456,"height":2426,"rx":2427,"fill":1534,"stroke":1535,"style":2441},"402.9",[290,2458,2461],{"x":2459,"y":2452,"style":2460},"605.2","font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:start","header drawn",[25,2463,2465],{"id":2464},"common-pitfalls","Common pitfalls",[2467,2468,2469,2476,2482,2492,2498],"ul",{},[2470,2471,2472,2475],"li",{},[1860,2473,2474],{},"A misspelt style name."," It is written without error and the table appears plain; validate names.",[2470,2477,2478,2481],{},[1860,2479,2480],{},"Expecting the same colours in every workbook."," Built-in styles use theme colours.",[2470,2483,2484,2489,2490,23],{},[1860,2485,2486,2488],{},[37,2487,1469],{}," in a table style."," Differential formats take a solid fill's colour from ",[37,2491,1253],{},[2470,2493,2494,2497],{},[1860,2495,2496],{},"Relying on table styles in LibreOffice or PDF exports."," They are not drawn; bake the look into cells.",[2470,2499,2500,2503],{},[1860,2501,2502],{},"Baked formatting in a table people sort."," Direct fills stay on their rows; keep the table style for live workbooks.",[25,2505,2507],{"id":2506},"conclusion","Conclusion",[10,2509,2510,2511,2514,2515,150,2518,2521],{},"An Excel table's look is a style name from the Light, Medium or Dark families plus four switches for row stripes, column stripes and first and last column emphasis. openpyxl sets them through ",[37,2512,2513],{},"TableStyleInfo",", XlsxWriter through ",[37,2516,2517],{},"style",[37,2519,2520],{},"banded_rows"," and friends, and openpyxl can also define a custom company style from differential formats. Validate style names, since typos pass silently, and when a workbook will be opened in LibreOffice or converted to PDF, copy the look into direct cell formatting because table styles are only drawn by Excel.",[25,2523,2525],{"id":2524},"frequently-asked-questions","Frequently asked questions",[10,2527,2528,2531],{},[1860,2529,2530],{},"How do I set the style of an Excel table in openpyxl?","\nPass a TableStyleInfo to the Table with a name such as TableStyleMedium9 and the switches showRowStripes, showColumnStripes, showFirstColumn and showLastColumn.",[10,2533,2534,2537],{},[1860,2535,2536],{},"What table style names does Excel have?","\nSixty built-in styles: TableStyleLight1 to Light21, TableStyleMedium1 to Medium28 and TableStyleDark1 to Dark11. openpyxl lists them in the TABLESTYLES tuple of its worksheet.table module.",[10,2539,2540,2543],{},[1860,2541,2542],{},"Can I create a custom table style with Python?","\nYes, with openpyxl, by adding differential formats and a TableStyle to the workbook through its private _differential_styles and _table_styles attributes. XlsxWriter does not support custom table styles.",[10,2545,2546,2549],{},[1860,2547,2548],{},"Why is my Excel table unstyled in LibreOffice?","\nLibreOffice does not draw Excel table styles. Copy the header and stripe formatting into the cells directly if the file will be opened there or converted to PDF with it.",[25,2551,2553],{"id":2552},"related","Related",[2467,2555,2556,2562,2567,2574],{},[2470,2557,2558,2559,2561],{},"Up one level: ",[14,2560,22],{"href":21}," — tables, filters and outlines.",[2470,2563,2564,2566],{},[14,2565,17],{"href":16}," — defining the table itself.",[2470,2568,2569,2573],{},[14,2570,2572],{"href":2571},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-excel-tables-and-autofilters-with-python\u002Fconvert-an-excel-table-back-to-a-normal-range\u002F","Convert an Excel Table Back to a Normal Range"," — removing a table but keeping its look.",[2470,2575,2576,2580],{},[14,2577,2579],{"href":2578},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl\u002Fchange-the-default-font-of-an-excel-workbook-with-python\u002F","Change the Default Font of an Excel Workbook with Python"," — workbook-wide styling.",[2517,2582,2583],{},"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 .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}",{"title":35,"searchDepth":108,"depth":108,"links":2585},[2586,2587,2588,2589,2590,2591,2592,2593,2594,2595,2596],{"id":27,"depth":108,"text":28},{"id":74,"depth":108,"text":75},{"id":375,"depth":108,"text":376},{"id":632,"depth":108,"text":633},{"id":918,"depth":108,"text":919},{"id":1609,"depth":108,"text":1610},{"id":1854,"depth":108,"text":1855},{"id":2464,"depth":108,"text":2465},{"id":2506,"depth":108,"text":2507},{"id":2524,"depth":108,"text":2525},{"id":2552,"depth":108,"text":2553},"2026-10-02","Pick the right look for an Excel table from Python: the 60 built-in styles and how their names work, the four banding and emphasis switches, styles in openpyxl and XlsxWriter, a custom company table style written with openpyxl, why typos in style names pass silently, and why LibreOffice and PDF conversions show tables unstyled unless you add direct formatting.","md",[2601,2603,2605,2607],{"q":2530,"a":2602},"Pass a TableStyleInfo to the Table with a name such as TableStyleMedium9 and the switches showRowStripes, showColumnStripes, showFirstColumn and showLastColumn.",{"q":2536,"a":2604},"Sixty built-in styles: TableStyleLight1 to Light21, TableStyleMedium1 to Medium28 and TableStyleDark1 to Dark11. openpyxl lists them in the TABLESTYLES tuple of its worksheet.table module.",{"q":2542,"a":2606},"Yes, with openpyxl, by adding differential formats and a TableStyle to the workbook through its private _differential_styles and _table_styles attributes. XlsxWriter does not support custom table styles.",{"q":2548,"a":2608},"LibreOffice does not draw Excel table styles. Copy the header and stripe formatting into the cells directly if the file will be opened there or converted to PDF with it.","choose and customise excel table styles with python",null,{"breadcrumb":2612},[2613,2615,2618],{"name":2614,"item":2299},"Home",{"name":2616,"item":2617},"Formatting and Charting Excel Reports with Python","\u002Fformatting-and-charting-excel-reports-with-python\u002F",{"name":22,"item":21},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-excel-tables-and-autofilters-with-python\u002Fchoose-and-customise-excel-table-styles-with-python",{"title":5,"description":2621},"Set Excel table styles from openpyxl and XlsxWriter: Light, Medium and Dark families, stripes and first\u002Flast column switches, a custom brand table style, catching bad names, and styling that survives LibreOffice.","choose-and-customise-excel-table-styles-with-python","formatting-and-charting-excel-reports-with-python\u002Fcreating-excel-tables-and-autofilters-with-python\u002Fchoose-and-customise-excel-table-styles-with-python\u002Findex",[2625,2626],"tables-and-filters","cell-styling","how-to","CwEkrQk2tvp7tMFT4yxNoFl1fWmfJhy6d3Z2_0gjD7E",[2630,2634],{"title":2631,"path":2632,"stem":2633,"children":-1},"Apply Autofilter Criteria and Hide the Filtered Rows with openpyxl","\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-excel-tables-and-autofilters-with-python\u002Fapply-autofilter-criteria-and-hide-rows","formatting-and-charting-excel-reports-with-python\u002Fcreating-excel-tables-and-autofilters-with-python\u002Fapply-autofilter-criteria-and-hide-rows\u002Findex",{"title":2572,"path":2635,"stem":2636,"children":-1},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-excel-tables-and-autofilters-with-python\u002Fconvert-an-excel-table-back-to-a-normal-range","formatting-and-charting-excel-reports-with-python\u002Fcreating-excel-tables-and-autofilters-with-python\u002Fconvert-an-excel-table-back-to-a-normal-range\u002Findex",[2638,2640],{"path":2639,"title":22},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-excel-tables-and-autofilters-with-python",{"path":2641,"title":2616},"\u002Fformatting-and-charting-excel-reports-with-python",[],[2644,2647,2650,2653,2656,2659],{"path":2645,"title":2646,"kind":2627},"\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":2648,"title":2649,"kind":2627},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fsort-and-filter-an-excel-online-table-with-microsoft-graph","Sort and Filter an Excel Online Table with Microsoft Graph",{"path":2651,"title":2652,"kind":2627},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fwrite-an-excel-table-with-formula-columns-using-xlsxwriter","Write an Excel Table with Formula Columns Using XlsxWriter",{"path":2654,"title":2655,"kind":2627},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fmake-an-excel-chart-grow-with-its-data","Make an Excel Chart Grow with Its Data",{"path":2657,"title":2658,"kind":2627},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-excel-tables-and-autofilters-with-python\u002Fadd-a-total-row-to-an-excel-table","Add a Total Row to an Excel Table with Python",{"path":2660,"title":2661,"kind":2627},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-excel-tables-and-autofilters-with-python\u002Fadd-an-autofilter-to-an-excel-sheet-with-openpyxl","Add an Autofilter to an Excel Sheet with openpyxl",1790972116387]