[{"data":1,"prerenderedAt":2573},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fuse-theme-colours-and-tints-in-openpyxl":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fuse-theme-colours-and-tints-in-openpyxl":2538,"ancestors:\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fuse-theme-colours-and-tints-in-openpyxl":2547,"children:\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fuse-theme-colours-and-tints-in-openpyxl":2552,"related:\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fuse-theme-colours-and-tints-in-openpyxl":2553},{"id":4,"title":5,"body":6,"dateModified":2506,"datePublished":2506,"description":2507,"extension":2508,"faq":2509,"keyword":2518,"kind":2519,"meta":2520,"navigation":140,"path":2528,"seo":2529,"slug":2531,"stem":2532,"topics":2533,"type":2536,"__hash__":2537},"docs\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fuse-theme-colours-and-tints-in-openpyxl\u002Findex.md","Use Theme Colours and Tints in openpyxl",{"type":7,"value":8,"toc":2495},"minimark",[9,32,37,66,80,84,99,528,533,537,547,634,654,798,802,811,1668,1697,1779,1783,1786,2222,2244,2371,2375,2416,2420,2430,2434,2440,2446,2452,2458,2462,2491],[10,11,12,13,17,18,21,22,25,26,31],"p",{},"Excel's colour picker has two halves. The bottom row of ",[14,15,16],"em",{},"standard colours"," are fixed RGB values. The grid above — ",[14,19,20],{},"Theme Colors",", with columns such as ",[14,23,24],{},"Blue, Accent 1"," and lighter and darker shades beneath — is relative: those cells store \"accent 1, lightened 40%\", and their actual colour comes from the workbook's theme. Change the theme and every cell that uses a theme colour changes with it, which is how a report template can be switched from one brand palette to another in one step. openpyxl can write theme colours and tints too, but its API exposes the raw theme index, the ordering is not the one Excel's picker suggests, and reading a theme colour back gives you an index and a tint rather than a colour. This page writes theme colours, explains the index order and the tint arithmetic, resolves theme colours to hex values — checked against a rendered copy of the sheet — and rebrands a workbook by editing its theme. It belongs to ",[27,28,30],"a",{"href":29},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002F","Using openpyxl for Excel File Manipulation",".",[33,34,36],"h2",{"id":35},"prerequisites","Prerequisites",[38,39,44],"pre",{"className":40,"code":41,"language":42,"meta":43,"style":43},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install openpyxl pillow\n","bash","",[45,46,47],"code",{"__ignoreMap":43},[48,49,52,56,60,63],"span",{"class":50,"line":51},"line",1,[48,53,55],{"class":54},"sMTad","pip",[48,57,59],{"class":58},"srMev"," install",[48,61,62],{"class":58}," openpyxl",[48,64,65],{"class":58}," pillow\n",[10,67,68,69,72,73,76,77,31],{},"LibreOffice (",[45,70,71],{},"soffice",") and ",[45,74,75],{},"pdftoppm"," render the sheet so the colours can be compared with what the code expects. openpyxl writes the classic Office theme into new workbooks, whose Accent 1 is the blue ",[45,78,79],{},"4F81BD",[33,81,83],{"id":82},"step-1-write-theme-colours-with-tints","Step 1 — Write theme colours with tints",[10,85,86,87,90,91,94,95,98],{},"A theme colour is an openpyxl ",[45,88,89],{},"Color"," with a ",[45,92,93],{},"theme"," index and an optional ",[45,96,97],{},"tint",":",[38,100,104],{"className":101,"code":102,"language":103,"meta":43,"style":43},"language-python shiki shiki-themes github-light github-dark-high-contrast","from openpyxl import Workbook\nfrom openpyxl.styles import Color, Font, PatternFill\n\nSWATCHES = [(\"Accent 1\", 4, 0.0), (\"Accent 1, lighter 40%\", 4, 0.4), (\"Accent 1, lighter 80%\", 4, 0.8),\n            (\"Accent 1, darker 25%\", 4, -0.25), (\"Accent 2\", 5, 0.0), (\"Accent 2, lighter 60%\", 5, 0.6),\n            (\"Accent 6, darker 50%\", 9, -0.5), (\"Text 1, lighter 50%\", 1, 0.5)]\n\nwb = Workbook()\nws = wb.active\nws.title = \"Swatches\"\nfor row, (label, theme, tint) in enumerate(SWATCHES, start=1):\n    ws.cell(row, 1, label)\n    ws.cell(row, 2).fill = PatternFill(\"solid\", fgColor=Color(theme=theme, tint=tint))\n    ws.row_dimensions[row].height = 24\nws[\"C1\"].font = Font(color=Color(theme=4), bold=True)\nws[\"C1\"] = \"Text in Accent 1\"\nws.column_dimensions[\"A\"].width = 26\nws.column_dimensions[\"B\"].width = 14\nwb.save(\"theme_colours.xlsx\")\n","python",[45,105,106,122,135,142,201,252,289,294,306,317,328,361,372,415,426,470,485,502,517],{"__ignoreMap":43},[48,107,108,112,116,119],{"class":50,"line":51},[48,109,111],{"class":110},"s-kum","from",[48,113,115],{"class":114},"skGVy"," openpyxl ",[48,117,118],{"class":110},"import",[48,120,121],{"class":114}," Workbook\n",[48,123,125,127,130,132],{"class":50,"line":124},2,[48,126,111],{"class":110},[48,128,129],{"class":114}," openpyxl.styles ",[48,131,118],{"class":110},[48,133,134],{"class":114}," Color, Font, PatternFill\n",[48,136,138],{"class":50,"line":137},3,[48,139,141],{"emptyLinePlaceholder":140},true,"\n",[48,143,145,149,152,155,158,161,164,166,169,172,175,177,179,181,184,186,189,191,193,195,198],{"class":50,"line":144},4,[48,146,148],{"class":147},"sP0c6","SWATCHES",[48,150,151],{"class":110}," =",[48,153,154],{"class":114}," [(",[48,156,157],{"class":58},"\"Accent 1\"",[48,159,160],{"class":114},", ",[48,162,163],{"class":147},"4",[48,165,160],{"class":114},[48,167,168],{"class":147},"0.0",[48,170,171],{"class":114},"), (",[48,173,174],{"class":58},"\"Accent 1, lighter 40%\"",[48,176,160],{"class":114},[48,178,163],{"class":147},[48,180,160],{"class":114},[48,182,183],{"class":147},"0.4",[48,185,171],{"class":114},[48,187,188],{"class":58},"\"Accent 1, lighter 80%\"",[48,190,160],{"class":114},[48,192,163],{"class":147},[48,194,160],{"class":114},[48,196,197],{"class":147},"0.8",[48,199,200],{"class":114},"),\n",[48,202,204,207,210,212,214,216,219,222,224,227,229,232,234,236,238,241,243,245,247,250],{"class":50,"line":203},5,[48,205,206],{"class":114},"            (",[48,208,209],{"class":58},"\"Accent 1, darker 25%\"",[48,211,160],{"class":114},[48,213,163],{"class":147},[48,215,160],{"class":114},[48,217,218],{"class":110},"-",[48,220,221],{"class":147},"0.25",[48,223,171],{"class":114},[48,225,226],{"class":58},"\"Accent 2\"",[48,228,160],{"class":114},[48,230,231],{"class":147},"5",[48,233,160],{"class":114},[48,235,168],{"class":147},[48,237,171],{"class":114},[48,239,240],{"class":58},"\"Accent 2, lighter 60%\"",[48,242,160],{"class":114},[48,244,231],{"class":147},[48,246,160],{"class":114},[48,248,249],{"class":147},"0.6",[48,251,200],{"class":114},[48,253,255,257,260,262,265,267,269,272,274,277,279,282,284,286],{"class":50,"line":254},6,[48,256,206],{"class":114},[48,258,259],{"class":58},"\"Accent 6, darker 50%\"",[48,261,160],{"class":114},[48,263,264],{"class":147},"9",[48,266,160],{"class":114},[48,268,218],{"class":110},[48,270,271],{"class":147},"0.5",[48,273,171],{"class":114},[48,275,276],{"class":58},"\"Text 1, lighter 50%\"",[48,278,160],{"class":114},[48,280,281],{"class":147},"1",[48,283,160],{"class":114},[48,285,271],{"class":147},[48,287,288],{"class":114},")]\n",[48,290,292],{"class":50,"line":291},7,[48,293,141],{"emptyLinePlaceholder":140},[48,295,297,300,303],{"class":50,"line":296},8,[48,298,299],{"class":114},"wb ",[48,301,302],{"class":110},"=",[48,304,305],{"class":114}," Workbook()\n",[48,307,309,312,314],{"class":50,"line":308},9,[48,310,311],{"class":114},"ws ",[48,313,302],{"class":110},[48,315,316],{"class":114}," wb.active\n",[48,318,320,323,325],{"class":50,"line":319},10,[48,321,322],{"class":114},"ws.title ",[48,324,302],{"class":110},[48,326,327],{"class":58}," \"Swatches\"\n",[48,329,331,334,337,340,343,346,348,350,354,356,358],{"class":50,"line":330},11,[48,332,333],{"class":110},"for",[48,335,336],{"class":114}," row, (label, theme, tint) ",[48,338,339],{"class":110},"in",[48,341,342],{"class":147}," enumerate",[48,344,345],{"class":114},"(",[48,347,148],{"class":147},[48,349,160],{"class":114},[48,351,353],{"class":352},"sa561","start",[48,355,302],{"class":110},[48,357,281],{"class":147},[48,359,360],{"class":114},"):\n",[48,362,364,367,369],{"class":50,"line":363},12,[48,365,366],{"class":114},"    ws.cell(row, ",[48,368,281],{"class":147},[48,370,371],{"class":114},", label)\n",[48,373,375,377,380,383,385,388,391,393,396,398,401,403,405,408,410,412],{"class":50,"line":374},13,[48,376,366],{"class":114},[48,378,379],{"class":147},"2",[48,381,382],{"class":114},").fill ",[48,384,302],{"class":110},[48,386,387],{"class":114}," PatternFill(",[48,389,390],{"class":58},"\"solid\"",[48,392,160],{"class":114},[48,394,395],{"class":352},"fgColor",[48,397,302],{"class":110},[48,399,400],{"class":114},"Color(",[48,402,93],{"class":352},[48,404,302],{"class":110},[48,406,407],{"class":114},"theme, ",[48,409,97],{"class":352},[48,411,302],{"class":110},[48,413,414],{"class":114},"tint))\n",[48,416,418,421,423],{"class":50,"line":417},14,[48,419,420],{"class":114},"    ws.row_dimensions[row].height ",[48,422,302],{"class":110},[48,424,425],{"class":147}," 24\n",[48,427,429,432,435,438,440,443,446,448,450,452,454,456,459,462,464,467],{"class":50,"line":428},15,[48,430,431],{"class":114},"ws[",[48,433,434],{"class":58},"\"C1\"",[48,436,437],{"class":114},"].font ",[48,439,302],{"class":110},[48,441,442],{"class":114}," Font(",[48,444,445],{"class":352},"color",[48,447,302],{"class":110},[48,449,400],{"class":114},[48,451,93],{"class":352},[48,453,302],{"class":110},[48,455,163],{"class":147},[48,457,458],{"class":114},"), ",[48,460,461],{"class":352},"bold",[48,463,302],{"class":110},[48,465,466],{"class":147},"True",[48,468,469],{"class":114},")\n",[48,471,473,475,477,480,482],{"class":50,"line":472},16,[48,474,431],{"class":114},[48,476,434],{"class":58},[48,478,479],{"class":114},"] ",[48,481,302],{"class":110},[48,483,484],{"class":58}," \"Text in Accent 1\"\n",[48,486,488,491,494,497,499],{"class":50,"line":487},17,[48,489,490],{"class":114},"ws.column_dimensions[",[48,492,493],{"class":58},"\"A\"",[48,495,496],{"class":114},"].width ",[48,498,302],{"class":110},[48,500,501],{"class":147}," 26\n",[48,503,505,507,510,512,514],{"class":50,"line":504},18,[48,506,490],{"class":114},[48,508,509],{"class":58},"\"B\"",[48,511,496],{"class":114},[48,513,302],{"class":110},[48,515,516],{"class":147}," 14\n",[48,518,520,523,526],{"class":50,"line":519},19,[48,521,522],{"class":114},"wb.save(",[48,524,525],{"class":58},"\"theme_colours.xlsx\"",[48,527,469],{"class":114},[10,529,530,532],{},[45,531,97],{}," runs from −1 to 1: positive values lighten towards white, negative values darken towards black, and Excel's picker uses the steps 0.8, 0.6 and 0.4 for \"lighter 80\u002F60\u002F40%\" and −0.25 and −0.5 for \"darker 25\u002F50%\". Theme colours work anywhere openpyxl takes a colour — fills, fonts, borders, chart elements — so a whole report can be styled from the theme. Excel shows such a cell's colour in the picker with the theme swatch selected, so people editing the report later naturally pick from the same palette instead of adding near-matching fixed colours. Conditional formatting rules can use theme colours in the same way.",[33,534,536],{"id":535},"step-2-the-index-order","Step 2 — The index order",[10,538,539,540,542,543,546],{},"The ",[45,541,93],{}," index counts the twelve colours of the theme's colour scheme, but the first four are ",[14,544,545],{},"swapped in pairs"," compared with the order in the theme file:",[548,549,550,566],"table",{},[551,552,553],"thead",{},[554,555,556,560,563],"tr",{},[557,558,559],"th",{},"index",[557,561,562],{},"name in the theme file",[557,564,565],{},"Excel picker",[567,568,569,581,591,601,612,623],"tbody",{},[554,570,571,575,578],{},[572,573,574],"td",{},"0",[572,576,577],{},"lt1",[572,579,580],{},"Background 1 (usually white)",[554,582,583,585,588],{},[572,584,281],{},[572,586,587],{},"dk1",[572,589,590],{},"Text 1 (usually black)",[554,592,593,595,598],{},[572,594,379],{},[572,596,597],{},"lt2",[572,599,600],{},"Background 2",[554,602,603,606,609],{},[572,604,605],{},"3",[572,607,608],{},"dk2",[572,610,611],{},"Text 2",[554,613,614,617,620],{},[572,615,616],{},"4–9",[572,618,619],{},"accent1–accent6",[572,621,622],{},"Accent 1–6",[554,624,625,628,631],{},[572,626,627],{},"10, 11",[572,629,630],{},"hlink, folHlink",[572,632,633],{},"hyperlink, followed hyperlink",[10,635,636,637,160,639,160,641,160,643,645,646,649,650,653],{},"The theme file lists the dark colour first (",[45,638,587],{},[45,640,577],{},[45,642,608],{},[45,644,597],{},"), but cell colours use index 0 for the ",[14,647,648],{},"light"," colour and 1 for the dark one. Getting this wrong is a classic bug: ",[45,651,652],{},"Color(theme=0)"," for \"text\" gives white text on a white background. Accents need no translation: Accent 1 is index 4 and Accent 6 is index 9.",[655,656,664,665,664,669,664,673,664,679,664,687,664,694,664,701,664,704,664,707,664,712,664,717,664,719,664,723,664,725,664,728,664,731,664,734,664,736,664,739,664,741,664,744,664,747,664,751,664,753,664,757,664,759,664,762,664,765,664,769,664,771,664,775,664,777,664,780,664,783,664,787,664,789,664,793,664,795],"svg",{"viewBox":657,"role":658,"ariaLabelledBy":659,"xmlns":662,"style":663},"4 0 752 242","img",[660,661],"tc-index-t","tc-index-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  ",[666,667,668],"title",{"id":660},"Theme colour indexes",[670,671,672],"desc",{"id":661},"Index 0 is the light colour lt1, Background 1, and index 1 the dark colour dk1, Text 1, even though the theme file lists dk1 first. Indexes 2 and 3 are Background 2 and Text 2. Indexes 4 to 9 are Accent 1 to 6, and 10 and 11 the hyperlink colours.",[674,675],"rect",{"x":163,"y":574,"width":676,"height":677,"fill":678},"752","242","#ffffff",[674,680],{"x":681,"y":682,"width":683,"height":684,"rx":574,"fill":678,"stroke":685,"style":686},"20.0","16.0","65.1","40.0","var(--line,#cdd5e6)","stroke-width:1px",[674,688],{"x":689,"y":682,"width":690,"height":684,"rx":574,"fill":691,"stroke":692,"style":693},"85.1","327.4","#f0f4ff","var(--brand,#5b5cf0)","stroke-width:1.2px",[695,696,700],"text",{"x":697,"y":698,"style":699},"248.8","41.0","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","theme file name",[674,702],{"x":703,"y":682,"width":690,"height":684,"rx":574,"fill":691,"stroke":692,"style":693},"412.6",[695,705,565],{"x":706,"y":698,"style":699},"576.3",[674,708],{"x":681,"y":709,"width":683,"height":710,"rx":574,"fill":711,"stroke":685,"style":686},"56.0","34.0","#e7ebef",[695,713,574],{"x":714,"y":715,"style":716},"32.0","78.0","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:start",[674,718],{"x":689,"y":709,"width":690,"height":710,"rx":574,"fill":678,"stroke":685,"style":686},[695,720,577],{"x":697,"y":721,"style":722},"77.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle",[674,724],{"x":703,"y":709,"width":690,"height":710,"rx":574,"fill":678,"stroke":685,"style":686},[695,726,727],{"x":706,"y":721,"style":722},"Background 1",[674,729],{"x":681,"y":730,"width":683,"height":710,"rx":574,"fill":711,"stroke":685,"style":686},"90.0",[695,732,281],{"x":714,"y":733,"style":716},"112.0",[674,735],{"x":689,"y":730,"width":690,"height":710,"rx":574,"fill":678,"stroke":685,"style":686},[695,737,587],{"x":697,"y":738,"style":722},"111.0",[674,740],{"x":703,"y":730,"width":690,"height":710,"rx":574,"fill":678,"stroke":685,"style":686},[695,742,743],{"x":706,"y":738,"style":722},"Text 1",[674,745],{"x":681,"y":746,"width":683,"height":710,"rx":574,"fill":711,"stroke":685,"style":686},"124.0",[695,748,750],{"x":714,"y":749,"style":716},"146.0","2 \u002F 3",[674,752],{"x":689,"y":746,"width":690,"height":710,"rx":574,"fill":678,"stroke":685,"style":686},[695,754,756],{"x":697,"y":755,"style":722},"145.0","lt2 \u002F dk2",[674,758],{"x":703,"y":746,"width":690,"height":710,"rx":574,"fill":678,"stroke":685,"style":686},[695,760,761],{"x":706,"y":755,"style":722},"Background 2 \u002F Text 2",[674,763],{"x":681,"y":764,"width":683,"height":710,"rx":574,"fill":711,"stroke":685,"style":686},"158.0",[695,766,768],{"x":714,"y":767,"style":716},"180.0","4 – 9",[674,770],{"x":689,"y":764,"width":690,"height":710,"rx":574,"fill":678,"stroke":685,"style":686},[695,772,774],{"x":697,"y":773,"style":722},"179.0","accent1 – accent6",[674,776],{"x":703,"y":764,"width":690,"height":710,"rx":574,"fill":678,"stroke":685,"style":686},[695,778,779],{"x":706,"y":773,"style":722},"Accent 1 – 6",[674,781],{"x":681,"y":782,"width":683,"height":710,"rx":574,"fill":711,"stroke":685,"style":686},"192.0",[695,784,786],{"x":714,"y":785,"style":716},"214.0","10 \u002F 11",[674,788],{"x":689,"y":782,"width":690,"height":710,"rx":574,"fill":678,"stroke":685,"style":686},[695,790,792],{"x":697,"y":791,"style":722},"213.0","hlink \u002F folHlink",[674,794],{"x":703,"y":782,"width":690,"height":710,"rx":574,"fill":678,"stroke":685,"style":686},[695,796,797],{"x":706,"y":791,"style":722},"hyperlinks",[33,799,801],{"id":800},"step-3-resolve-theme-colours-to-hex-and-check-them","Step 3 — Resolve theme colours to hex, and check them",[10,803,804,805,807,808,810],{},"Reading a cell's colour back gives ",[45,806,93],{}," and ",[45,809,97],{},", not RGB. To show the colour elsewhere — in an HTML version of the report, a legend, a test — resolve it from the workbook's theme and apply the tint the way Excel does, by changing luminance in HLS colour space:",[38,812,814],{"className":101,"code":813,"language":103,"meta":43,"style":43},"import colorsys, re, subprocess\nfrom openpyxl import load_workbook\nfrom PIL import Image\n\nORDER = [\"lt1\", \"dk1\", \"lt2\", \"dk2\", \"accent1\", \"accent2\", \"accent3\", \"accent4\", \"accent5\", \"accent6\", \"hlink\", \"folHlink\"]\n\ndef theme_palette(wb):\n    xml = wb.loaded_theme.decode()\n    return dict(re.findall(r\"\u003Ca:(dk1|lt1|dk2|lt2|accent\\d|hlink|folHlink)>.*?(?:srgbClr val|lastClr)=\\\"([0-9A-Fa-f]{6})\\\"\", xml, re.S))\n\ndef resolve(palette, theme, tint=0.0):\n    base = palette[ORDER[theme]]\n    h, l, s = colorsys.rgb_to_hls(*(int(base[i:i + 2], 16) \u002F 255 for i in (0, 2, 4)))\n    l = l * (1 + tint) if tint \u003C 0 else l * (1 - tint) + tint\n    return \"\".join(f\"{round(v * 255):02X}\" for v in colorsys.hls_to_rgb(h, l, s))\n\nsubprocess.run([\"soffice\", \"--headless\", \"--convert-to\", \"pdf\", \"theme_colours.xlsx\"], check=True, capture_output=True)\nsubprocess.run([\"pdftoppm\", \"-png\", \"-r\", \"72\", \"-f\", \"1\", \"-l\", \"1\", \"theme_colours.pdf\", \"swatches\"], check=True)\nrendered = {c for n, c in Image.open(\"swatches-1.png\").convert(\"RGB\").getcolors(maxcolors=10**6) if n > 100}\n\nwb = load_workbook(\"theme_colours.xlsx\")\npalette = theme_palette(wb)\nfor row in wb[\"Swatches\"].iter_rows(min_row=1, max_row=len(SWATCHES)):\n    colour = row[1].fill.fgColor\n    hexval = resolve(palette, colour.theme, colour.tint)\n    found = tuple(int(hexval[i:i + 2], 16) for i in (0, 2, 4)) in rendered\n    print(f\"{row[0].value:\u003C24} theme={colour.theme} tint={colour.tint:+.2f} -> #{hexval}  {'in render' if found else 'NOT FOUND'}\")\n",[45,815,816,823,834,847,851,922,926,938,948,1053,1057,1073,1088,1154,1209,1257,1261,1308,1369,1430,1435,1449,1460,1503,1519,1530,1584],{"__ignoreMap":43},[48,817,818,820],{"class":50,"line":51},[48,819,118],{"class":110},[48,821,822],{"class":114}," colorsys, re, subprocess\n",[48,824,825,827,829,831],{"class":50,"line":124},[48,826,111],{"class":110},[48,828,115],{"class":114},[48,830,118],{"class":110},[48,832,833],{"class":114}," load_workbook\n",[48,835,836,838,841,844],{"class":50,"line":137},[48,837,111],{"class":110},[48,839,840],{"class":147}," PIL",[48,842,843],{"class":110}," import",[48,845,846],{"class":114}," Image\n",[48,848,849],{"class":50,"line":144},[48,850,141],{"emptyLinePlaceholder":140},[48,852,853,856,858,861,864,866,869,871,874,876,879,881,884,886,889,891,894,896,899,901,904,906,909,911,914,916,919],{"class":50,"line":203},[48,854,855],{"class":147},"ORDER",[48,857,151],{"class":110},[48,859,860],{"class":114}," [",[48,862,863],{"class":58},"\"lt1\"",[48,865,160],{"class":114},[48,867,868],{"class":58},"\"dk1\"",[48,870,160],{"class":114},[48,872,873],{"class":58},"\"lt2\"",[48,875,160],{"class":114},[48,877,878],{"class":58},"\"dk2\"",[48,880,160],{"class":114},[48,882,883],{"class":58},"\"accent1\"",[48,885,160],{"class":114},[48,887,888],{"class":58},"\"accent2\"",[48,890,160],{"class":114},[48,892,893],{"class":58},"\"accent3\"",[48,895,160],{"class":114},[48,897,898],{"class":58},"\"accent4\"",[48,900,160],{"class":114},[48,902,903],{"class":58},"\"accent5\"",[48,905,160],{"class":114},[48,907,908],{"class":58},"\"accent6\"",[48,910,160],{"class":114},[48,912,913],{"class":58},"\"hlink\"",[48,915,160],{"class":114},[48,917,918],{"class":58},"\"folHlink\"",[48,920,921],{"class":114},"]\n",[48,923,924],{"class":50,"line":254},[48,925,141],{"emptyLinePlaceholder":140},[48,927,928,931,935],{"class":50,"line":291},[48,929,930],{"class":110},"def",[48,932,934],{"class":933},"s_Opv"," theme_palette",[48,936,937],{"class":114},"(wb):\n",[48,939,940,943,945],{"class":50,"line":296},[48,941,942],{"class":114},"    xml ",[48,944,302],{"class":110},[48,946,947],{"class":114}," wb.loaded_theme.decode()\n",[48,949,950,953,956,959,962,965,967,969,972,974,976,978,980,982,984,987,990,992,995,997,1000,1003,1006,1008,1011,1014,1017,1019,1022,1024,1026,1030,1033,1037,1040,1043,1045,1047,1050],{"class":50,"line":308},[48,951,952],{"class":110},"    return",[48,954,955],{"class":147}," dict",[48,957,958],{"class":114},"(re.findall(",[48,960,961],{"class":110},"r",[48,963,964],{"class":58},"\"\u003Ca:",[48,966,345],{"class":147},[48,968,587],{"class":58},[48,970,971],{"class":110},"|",[48,973,577],{"class":58},[48,975,971],{"class":110},[48,977,608],{"class":58},[48,979,971],{"class":110},[48,981,597],{"class":58},[48,983,971],{"class":110},[48,985,986],{"class":58},"accent",[48,988,989],{"class":147},"\\d",[48,991,971],{"class":110},[48,993,994],{"class":58},"hlink",[48,996,971],{"class":110},[48,998,999],{"class":58},"folHlink",[48,1001,1002],{"class":147},")",[48,1004,1005],{"class":58},">",[48,1007,31],{"class":147},[48,1009,1010],{"class":110},"*?",[48,1012,1013],{"class":147},"(?:",[48,1015,1016],{"class":58},"srgbClr val",[48,1018,971],{"class":110},[48,1020,1021],{"class":58},"lastClr",[48,1023,1002],{"class":147},[48,1025,302],{"class":58},[48,1027,1029],{"class":1028},"s_b0D","\\\"",[48,1031,1032],{"class":147},"([",[48,1034,1036],{"class":1035},"sSjpA","0-9A-Fa-f",[48,1038,1039],{"class":147},"]",[48,1041,1042],{"class":110},"{6}",[48,1044,1002],{"class":147},[48,1046,1029],{"class":1028},[48,1048,1049],{"class":58},"\"",[48,1051,1052],{"class":114},", xml, re.S))\n",[48,1054,1055],{"class":50,"line":319},[48,1056,141],{"emptyLinePlaceholder":140},[48,1058,1059,1061,1064,1067,1069,1071],{"class":50,"line":330},[48,1060,930],{"class":110},[48,1062,1063],{"class":933}," resolve",[48,1065,1066],{"class":114},"(palette, theme, tint",[48,1068,302],{"class":110},[48,1070,168],{"class":147},[48,1072,360],{"class":114},[48,1074,1075,1078,1080,1083,1085],{"class":50,"line":363},[48,1076,1077],{"class":114},"    base ",[48,1079,302],{"class":110},[48,1081,1082],{"class":114}," palette[",[48,1084,855],{"class":147},[48,1086,1087],{"class":114},"[theme]]\n",[48,1089,1090,1093,1095,1098,1101,1103,1106,1109,1112,1115,1118,1121,1124,1127,1130,1133,1136,1138,1141,1143,1145,1147,1149,1151],{"class":50,"line":374},[48,1091,1092],{"class":114},"    h, l, s ",[48,1094,302],{"class":110},[48,1096,1097],{"class":114}," colorsys.rgb_to_hls(",[48,1099,1100],{"class":110},"*",[48,1102,345],{"class":114},[48,1104,1105],{"class":147},"int",[48,1107,1108],{"class":114},"(base[i:i ",[48,1110,1111],{"class":110},"+",[48,1113,1114],{"class":147}," 2",[48,1116,1117],{"class":114},"], ",[48,1119,1120],{"class":147},"16",[48,1122,1123],{"class":114},") ",[48,1125,1126],{"class":110},"\u002F",[48,1128,1129],{"class":147}," 255",[48,1131,1132],{"class":110}," for",[48,1134,1135],{"class":114}," i ",[48,1137,339],{"class":110},[48,1139,1140],{"class":114}," (",[48,1142,574],{"class":147},[48,1144,160],{"class":114},[48,1146,379],{"class":147},[48,1148,160],{"class":114},[48,1150,163],{"class":147},[48,1152,1153],{"class":114},")))\n",[48,1155,1156,1159,1161,1164,1166,1168,1170,1173,1176,1179,1182,1185,1188,1191,1193,1195,1197,1199,1202,1204,1206],{"class":50,"line":417},[48,1157,1158],{"class":114},"    l ",[48,1160,302],{"class":110},[48,1162,1163],{"class":114}," l ",[48,1165,1100],{"class":110},[48,1167,1140],{"class":114},[48,1169,281],{"class":147},[48,1171,1172],{"class":110}," +",[48,1174,1175],{"class":114}," tint) ",[48,1177,1178],{"class":110},"if",[48,1180,1181],{"class":114}," tint ",[48,1183,1184],{"class":110},"\u003C",[48,1186,1187],{"class":147}," 0",[48,1189,1190],{"class":110}," else",[48,1192,1163],{"class":114},[48,1194,1100],{"class":110},[48,1196,1140],{"class":114},[48,1198,281],{"class":147},[48,1200,1201],{"class":110}," -",[48,1203,1175],{"class":114},[48,1205,1111],{"class":110},[48,1207,1208],{"class":114}," tint\n",[48,1210,1211,1213,1216,1219,1222,1224,1227,1230,1233,1235,1237,1239,1242,1245,1247,1249,1252,1254],{"class":50,"line":428},[48,1212,952],{"class":110},[48,1214,1215],{"class":58}," \"\"",[48,1217,1218],{"class":114},".join(",[48,1220,1221],{"class":110},"f",[48,1223,1049],{"class":58},[48,1225,1226],{"class":1035},"{",[48,1228,1229],{"class":147},"round",[48,1231,1232],{"class":114},"(v ",[48,1234,1100],{"class":110},[48,1236,1129],{"class":147},[48,1238,1002],{"class":114},[48,1240,1241],{"class":110},":02X",[48,1243,1244],{"class":1035},"}",[48,1246,1049],{"class":58},[48,1248,1132],{"class":110},[48,1250,1251],{"class":114}," v ",[48,1253,339],{"class":110},[48,1255,1256],{"class":114}," colorsys.hls_to_rgb(h, l, s))\n",[48,1258,1259],{"class":50,"line":472},[48,1260,141],{"emptyLinePlaceholder":140},[48,1262,1263,1266,1269,1271,1274,1276,1279,1281,1284,1286,1288,1290,1293,1295,1297,1299,1302,1304,1306],{"class":50,"line":487},[48,1264,1265],{"class":114},"subprocess.run([",[48,1267,1268],{"class":58},"\"soffice\"",[48,1270,160],{"class":114},[48,1272,1273],{"class":58},"\"--headless\"",[48,1275,160],{"class":114},[48,1277,1278],{"class":58},"\"--convert-to\"",[48,1280,160],{"class":114},[48,1282,1283],{"class":58},"\"pdf\"",[48,1285,160],{"class":114},[48,1287,525],{"class":58},[48,1289,1117],{"class":114},[48,1291,1292],{"class":352},"check",[48,1294,302],{"class":110},[48,1296,466],{"class":147},[48,1298,160],{"class":114},[48,1300,1301],{"class":352},"capture_output",[48,1303,302],{"class":110},[48,1305,466],{"class":147},[48,1307,469],{"class":114},[48,1309,1310,1312,1315,1317,1320,1322,1325,1327,1330,1332,1335,1337,1340,1342,1345,1347,1349,1351,1354,1356,1359,1361,1363,1365,1367],{"class":50,"line":504},[48,1311,1265],{"class":114},[48,1313,1314],{"class":58},"\"pdftoppm\"",[48,1316,160],{"class":114},[48,1318,1319],{"class":58},"\"-png\"",[48,1321,160],{"class":114},[48,1323,1324],{"class":58},"\"-r\"",[48,1326,160],{"class":114},[48,1328,1329],{"class":58},"\"72\"",[48,1331,160],{"class":114},[48,1333,1334],{"class":58},"\"-f\"",[48,1336,160],{"class":114},[48,1338,1339],{"class":58},"\"1\"",[48,1341,160],{"class":114},[48,1343,1344],{"class":58},"\"-l\"",[48,1346,160],{"class":114},[48,1348,1339],{"class":58},[48,1350,160],{"class":114},[48,1352,1353],{"class":58},"\"theme_colours.pdf\"",[48,1355,160],{"class":114},[48,1357,1358],{"class":58},"\"swatches\"",[48,1360,1117],{"class":114},[48,1362,1292],{"class":352},[48,1364,302],{"class":110},[48,1366,466],{"class":147},[48,1368,469],{"class":114},[48,1370,1371,1374,1376,1379,1381,1384,1386,1389,1392,1395,1398,1401,1404,1406,1409,1412,1415,1417,1419,1422,1424,1427],{"class":50,"line":519},[48,1372,1373],{"class":114},"rendered ",[48,1375,302],{"class":110},[48,1377,1378],{"class":114}," {c ",[48,1380,333],{"class":110},[48,1382,1383],{"class":114}," n, c ",[48,1385,339],{"class":110},[48,1387,1388],{"class":114}," Image.open(",[48,1390,1391],{"class":58},"\"swatches-1.png\"",[48,1393,1394],{"class":114},").convert(",[48,1396,1397],{"class":58},"\"RGB\"",[48,1399,1400],{"class":114},").getcolors(",[48,1402,1403],{"class":352},"maxcolors",[48,1405,302],{"class":110},[48,1407,1408],{"class":147},"10",[48,1410,1411],{"class":110},"**",[48,1413,1414],{"class":147},"6",[48,1416,1123],{"class":114},[48,1418,1178],{"class":110},[48,1420,1421],{"class":114}," n ",[48,1423,1005],{"class":110},[48,1425,1426],{"class":147}," 100",[48,1428,1429],{"class":114},"}\n",[48,1431,1433],{"class":50,"line":1432},20,[48,1434,141],{"emptyLinePlaceholder":140},[48,1436,1438,1440,1442,1445,1447],{"class":50,"line":1437},21,[48,1439,299],{"class":114},[48,1441,302],{"class":110},[48,1443,1444],{"class":114}," load_workbook(",[48,1446,525],{"class":58},[48,1448,469],{"class":114},[48,1450,1452,1455,1457],{"class":50,"line":1451},22,[48,1453,1454],{"class":114},"palette ",[48,1456,302],{"class":110},[48,1458,1459],{"class":114}," theme_palette(wb)\n",[48,1461,1463,1465,1468,1470,1473,1476,1479,1482,1484,1486,1488,1491,1493,1496,1498,1500],{"class":50,"line":1462},23,[48,1464,333],{"class":110},[48,1466,1467],{"class":114}," row ",[48,1469,339],{"class":110},[48,1471,1472],{"class":114}," wb[",[48,1474,1475],{"class":58},"\"Swatches\"",[48,1477,1478],{"class":114},"].iter_rows(",[48,1480,1481],{"class":352},"min_row",[48,1483,302],{"class":110},[48,1485,281],{"class":147},[48,1487,160],{"class":114},[48,1489,1490],{"class":352},"max_row",[48,1492,302],{"class":110},[48,1494,1495],{"class":147},"len",[48,1497,345],{"class":114},[48,1499,148],{"class":147},[48,1501,1502],{"class":114},")):\n",[48,1504,1506,1509,1511,1514,1516],{"class":50,"line":1505},24,[48,1507,1508],{"class":114},"    colour ",[48,1510,302],{"class":110},[48,1512,1513],{"class":114}," row[",[48,1515,281],{"class":147},[48,1517,1518],{"class":114},"].fill.fgColor\n",[48,1520,1522,1525,1527],{"class":50,"line":1521},25,[48,1523,1524],{"class":114},"    hexval ",[48,1526,302],{"class":110},[48,1528,1529],{"class":114}," resolve(palette, colour.theme, colour.tint)\n",[48,1531,1533,1536,1538,1541,1543,1545,1548,1550,1552,1554,1556,1558,1560,1562,1564,1566,1568,1570,1572,1574,1576,1579,1581],{"class":50,"line":1532},26,[48,1534,1535],{"class":114},"    found ",[48,1537,302],{"class":110},[48,1539,1540],{"class":147}," tuple",[48,1542,345],{"class":114},[48,1544,1105],{"class":147},[48,1546,1547],{"class":114},"(hexval[i:i ",[48,1549,1111],{"class":110},[48,1551,1114],{"class":147},[48,1553,1117],{"class":114},[48,1555,1120],{"class":147},[48,1557,1123],{"class":114},[48,1559,333],{"class":110},[48,1561,1135],{"class":114},[48,1563,339],{"class":110},[48,1565,1140],{"class":114},[48,1567,574],{"class":147},[48,1569,160],{"class":114},[48,1571,379],{"class":147},[48,1573,160],{"class":114},[48,1575,163],{"class":147},[48,1577,1578],{"class":114},")) ",[48,1580,339],{"class":110},[48,1582,1583],{"class":114}," rendered\n",[48,1585,1587,1590,1592,1594,1596,1598,1601,1603,1606,1609,1611,1614,1616,1619,1621,1624,1626,1629,1632,1634,1637,1639,1642,1644,1647,1650,1653,1656,1659,1662,1664,1666],{"class":50,"line":1586},27,[48,1588,1589],{"class":147},"    print",[48,1591,345],{"class":114},[48,1593,1221],{"class":110},[48,1595,1049],{"class":58},[48,1597,1226],{"class":1035},[48,1599,1600],{"class":114},"row[",[48,1602,574],{"class":147},[48,1604,1605],{"class":114},"].value",[48,1607,1608],{"class":110},":\u003C24",[48,1610,1244],{"class":1035},[48,1612,1613],{"class":58}," theme=",[48,1615,1226],{"class":1035},[48,1617,1618],{"class":114},"colour.theme",[48,1620,1244],{"class":1035},[48,1622,1623],{"class":58}," tint=",[48,1625,1226],{"class":1035},[48,1627,1628],{"class":114},"colour.tint",[48,1630,1631],{"class":110},":+.2f",[48,1633,1244],{"class":1035},[48,1635,1636],{"class":58}," -> #",[48,1638,1226],{"class":1035},[48,1640,1641],{"class":114},"hexval",[48,1643,1244],{"class":1035},[48,1645,1646],{"class":1035},"  {",[48,1648,1649],{"class":58},"'in render'",[48,1651,1652],{"class":110}," if",[48,1654,1655],{"class":114}," found ",[48,1657,1658],{"class":110},"else",[48,1660,1661],{"class":58}," 'NOT FOUND'",[48,1663,1244],{"class":1035},[48,1665,1049],{"class":58},[48,1667,469],{"class":114},[10,1669,1670,1673,1674,1677,1678,1680,1681,1684,1685,1688,1689,1692,1693,1696],{},[45,1671,1672],{},"wb.loaded_theme"," holds the theme XML of a loaded workbook; the regular expression pulls out the twelve colours, whether stored as ",[45,1675,1676],{},"srgbClr"," values or, for the system colours, as ",[45,1679,1021],{},". The tint formula is Excel's: a negative tint multiplies the luminance by ",[45,1682,1683],{},"1 + tint",", a positive one moves it that fraction of the way towards white. Every resolved colour — ",[45,1686,1687],{},"#95B3D7"," for Accent 1 lighter 40%, ",[45,1690,1691],{},"#376092"," for darker 25%, ",[45,1694,1695],{},"#808080"," for Text 1 lighter 50% — appears in the rendered page, so the resolver reproduces the colours a spreadsheet application draws.",[655,1698,664,1704,664,1707,664,1710,664,1714,664,1719,664,1725,664,1730,664,1735,664,1737,664,1741,664,1744,664,1748,664,1751,664,1755,664,1760,664,1765,664,1768,664,1772,664,1775],{"viewBox":1699,"role":658,"ariaLabelledBy":1700,"xmlns":662,"style":1703},"4 0 744 180",[1701,1702],"tc-tints-t","tc-tints-d","width:100%;max-width:744px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[666,1705,1706],{"id":1701},"Accent 1 at different tints",[670,1708,1709],{"id":1702},"The base Accent 1 is 4F81BD. A tint of 0.4 gives 95B3D7 and 0.8 gives DCE6F2, both lighter. A tint of minus 0.25 gives the darker 376092. The bars show luminance rising with positive tints and falling with negative ones.",[674,1711],{"x":163,"y":574,"width":1712,"height":1713,"fill":678},"744","180",[674,1715],{"x":681,"y":682,"width":1716,"height":1717,"rx":1718,"fill":711,"stroke":685,"style":686},"167.5","28.0","7",[695,1720,1724],{"x":1721,"y":1722,"style":1723},"103.8","34.5","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","tint −0.25 → #376092",[674,1726],{"x":1727,"y":682,"width":1728,"height":1717,"rx":1718,"fill":691,"stroke":692,"style":1729},"197.5","197.9","stroke-width:1.5px",[695,1731,1734],{"x":1732,"y":1722,"style":1733},"403.4","font-size:12px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:start","darker",[674,1736],{"x":681,"y":709,"width":1716,"height":1717,"rx":1718,"fill":711,"stroke":685,"style":686},[695,1738,1740],{"x":1721,"y":1739,"style":1723},"74.5","tint 0 → #4F81BD",[674,1742],{"x":1727,"y":709,"width":1743,"height":1717,"rx":1718,"fill":691,"stroke":692,"style":1729},"268.9",[695,1745,1747],{"x":1746,"y":1739,"style":1733},"474.4","base",[674,1749],{"x":681,"y":1750,"width":1716,"height":1717,"rx":1718,"fill":711,"stroke":685,"style":686},"96.0",[695,1752,1754],{"x":1721,"y":1753,"style":1723},"114.5","tint 0.4 → #95B3D7",[674,1756],{"x":1727,"y":1750,"width":1757,"height":1717,"rx":1718,"fill":1758,"stroke":1759,"style":1729},"365.3","#d9f4f1","var(--teal,#0f9488)",[695,1761,1764],{"x":1762,"y":1753,"style":1763},"570.8","font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:start","lighter",[674,1766],{"x":681,"y":1767,"width":1716,"height":1717,"rx":1718,"fill":711,"stroke":685,"style":686},"136.0",[695,1769,1771],{"x":1721,"y":1770,"style":1723},"154.5","tint 0.8 → #DCE6F2",[674,1773],{"x":1727,"y":1767,"width":1774,"height":1717,"rx":1718,"fill":1758,"stroke":1759,"style":1729},"461.7",[695,1776,1778],{"x":1777,"y":1770,"style":1763},"667.2","lighter still",[33,1780,1782],{"id":1781},"step-4-rebrand-by-changing-the-theme","Step 4 — Rebrand by changing the theme",[10,1784,1785],{},"Because the cells store \"Accent 1\" rather than a colour, changing Accent 1 in the theme recolours the whole workbook. Replace the accent values in the theme XML and save:",[38,1787,1789],{"className":101,"code":1788,"language":103,"meta":43,"style":43},"BRAND = {\"accent1\": \"1F6FEB\", \"accent2\": \"D4A72C\"}\n\nxml = wb.loaded_theme.decode()\nfor name, hexval in BRAND.items():\n    xml = re.sub(rf\"(\u003Ca:{name}>\\s*\u003Ca:srgbClr val=\\\")[0-9A-Fa-f]{{6}}\", rf\"\\g\u003C1>{hexval}\", xml)\nwb.loaded_theme = xml.encode()\nwb.save(\"theme_colours_rebranded.xlsx\")\n\nrebranded = load_workbook(\"theme_colours_rebranded.xlsx\")\npalette = theme_palette(rebranded)\nsubprocess.run([\"soffice\", \"--headless\", \"--convert-to\", \"pdf\", \"theme_colours_rebranded.xlsx\"], check=True, capture_output=True)\nsubprocess.run([\"pdftoppm\", \"-png\", \"-r\", \"72\", \"-f\", \"1\", \"-l\", \"1\", \"theme_colours_rebranded.pdf\", \"rebrand\"], check=True)\nrendered = {c for n, c in Image.open(\"rebrand-1.png\").convert(\"RGB\").getcolors(maxcolors=10**6) if n > 100}\nfor label, theme, tint in SWATCHES[:3] + SWATCHES[4:6]:\n    hexval = resolve(palette, theme, tint)\n    print(f\"{label:\u003C24} -> #{hexval}  {'in render' if tuple(int(hexval[i:i + 2], 16) for i in (0, 2, 4)) in rendered else 'NOT FOUND'}\")\n",[45,1790,1791,1820,1824,1833,1848,1906,1916,1925,1929,1942,1951,1991,2045,2092,2127,2136],{"__ignoreMap":43},[48,1792,1793,1796,1798,1801,1803,1806,1809,1811,1813,1815,1818],{"class":50,"line":51},[48,1794,1795],{"class":147},"BRAND",[48,1797,151],{"class":110},[48,1799,1800],{"class":114}," {",[48,1802,883],{"class":58},[48,1804,1805],{"class":114},": ",[48,1807,1808],{"class":58},"\"1F6FEB\"",[48,1810,160],{"class":114},[48,1812,888],{"class":58},[48,1814,1805],{"class":114},[48,1816,1817],{"class":58},"\"D4A72C\"",[48,1819,1429],{"class":114},[48,1821,1822],{"class":50,"line":124},[48,1823,141],{"emptyLinePlaceholder":140},[48,1825,1826,1829,1831],{"class":50,"line":137},[48,1827,1828],{"class":114},"xml ",[48,1830,302],{"class":110},[48,1832,947],{"class":114},[48,1834,1835,1837,1840,1842,1845],{"class":50,"line":144},[48,1836,333],{"class":110},[48,1838,1839],{"class":114}," name, hexval ",[48,1841,339],{"class":110},[48,1843,1844],{"class":147}," BRAND",[48,1846,1847],{"class":114},".items():\n",[48,1849,1850,1852,1854,1857,1860,1863,1865,1868,1870,1873,1875,1878,1881,1883,1886,1888,1890,1892,1895,1897,1899,1901,1903],{"class":50,"line":203},[48,1851,942],{"class":114},[48,1853,302],{"class":110},[48,1855,1856],{"class":114}," re.sub(",[48,1858,1859],{"class":110},"rf",[48,1861,1862],{"class":58},"\"(\u003Ca:",[48,1864,1226],{"class":1035},[48,1866,1867],{"class":114},"name",[48,1869,1244],{"class":1035},[48,1871,1872],{"class":58},">\\s*\u003Ca:srgbClr val=",[48,1874,1029],{"class":114},[48,1876,1877],{"class":58},")[0-9A-Fa-f]",[48,1879,1880],{"class":1035},"{{",[48,1882,1414],{"class":58},[48,1884,1885],{"class":1035},"}}",[48,1887,1049],{"class":58},[48,1889,160],{"class":114},[48,1891,1859],{"class":110},[48,1893,1894],{"class":58},"\"\\g\u003C1>",[48,1896,1226],{"class":1035},[48,1898,1641],{"class":114},[48,1900,1244],{"class":1035},[48,1902,1049],{"class":58},[48,1904,1905],{"class":114},", xml)\n",[48,1907,1908,1911,1913],{"class":50,"line":254},[48,1909,1910],{"class":114},"wb.loaded_theme ",[48,1912,302],{"class":110},[48,1914,1915],{"class":114}," xml.encode()\n",[48,1917,1918,1920,1923],{"class":50,"line":291},[48,1919,522],{"class":114},[48,1921,1922],{"class":58},"\"theme_colours_rebranded.xlsx\"",[48,1924,469],{"class":114},[48,1926,1927],{"class":50,"line":296},[48,1928,141],{"emptyLinePlaceholder":140},[48,1930,1931,1934,1936,1938,1940],{"class":50,"line":308},[48,1932,1933],{"class":114},"rebranded ",[48,1935,302],{"class":110},[48,1937,1444],{"class":114},[48,1939,1922],{"class":58},[48,1941,469],{"class":114},[48,1943,1944,1946,1948],{"class":50,"line":319},[48,1945,1454],{"class":114},[48,1947,302],{"class":110},[48,1949,1950],{"class":114}," theme_palette(rebranded)\n",[48,1952,1953,1955,1957,1959,1961,1963,1965,1967,1969,1971,1973,1975,1977,1979,1981,1983,1985,1987,1989],{"class":50,"line":330},[48,1954,1265],{"class":114},[48,1956,1268],{"class":58},[48,1958,160],{"class":114},[48,1960,1273],{"class":58},[48,1962,160],{"class":114},[48,1964,1278],{"class":58},[48,1966,160],{"class":114},[48,1968,1283],{"class":58},[48,1970,160],{"class":114},[48,1972,1922],{"class":58},[48,1974,1117],{"class":114},[48,1976,1292],{"class":352},[48,1978,302],{"class":110},[48,1980,466],{"class":147},[48,1982,160],{"class":114},[48,1984,1301],{"class":352},[48,1986,302],{"class":110},[48,1988,466],{"class":147},[48,1990,469],{"class":114},[48,1992,1993,1995,1997,1999,2001,2003,2005,2007,2009,2011,2013,2015,2017,2019,2021,2023,2025,2027,2030,2032,2035,2037,2039,2041,2043],{"class":50,"line":363},[48,1994,1265],{"class":114},[48,1996,1314],{"class":58},[48,1998,160],{"class":114},[48,2000,1319],{"class":58},[48,2002,160],{"class":114},[48,2004,1324],{"class":58},[48,2006,160],{"class":114},[48,2008,1329],{"class":58},[48,2010,160],{"class":114},[48,2012,1334],{"class":58},[48,2014,160],{"class":114},[48,2016,1339],{"class":58},[48,2018,160],{"class":114},[48,2020,1344],{"class":58},[48,2022,160],{"class":114},[48,2024,1339],{"class":58},[48,2026,160],{"class":114},[48,2028,2029],{"class":58},"\"theme_colours_rebranded.pdf\"",[48,2031,160],{"class":114},[48,2033,2034],{"class":58},"\"rebrand\"",[48,2036,1117],{"class":114},[48,2038,1292],{"class":352},[48,2040,302],{"class":110},[48,2042,466],{"class":147},[48,2044,469],{"class":114},[48,2046,2047,2049,2051,2053,2055,2057,2059,2061,2064,2066,2068,2070,2072,2074,2076,2078,2080,2082,2084,2086,2088,2090],{"class":50,"line":374},[48,2048,1373],{"class":114},[48,2050,302],{"class":110},[48,2052,1378],{"class":114},[48,2054,333],{"class":110},[48,2056,1383],{"class":114},[48,2058,339],{"class":110},[48,2060,1388],{"class":114},[48,2062,2063],{"class":58},"\"rebrand-1.png\"",[48,2065,1394],{"class":114},[48,2067,1397],{"class":58},[48,2069,1400],{"class":114},[48,2071,1403],{"class":352},[48,2073,302],{"class":110},[48,2075,1408],{"class":147},[48,2077,1411],{"class":110},[48,2079,1414],{"class":147},[48,2081,1123],{"class":114},[48,2083,1178],{"class":110},[48,2085,1421],{"class":114},[48,2087,1005],{"class":110},[48,2089,1426],{"class":147},[48,2091,1429],{"class":114},[48,2093,2094,2096,2099,2101,2104,2107,2109,2111,2113,2115,2118,2120,2122,2124],{"class":50,"line":417},[48,2095,333],{"class":110},[48,2097,2098],{"class":114}," label, theme, tint ",[48,2100,339],{"class":110},[48,2102,2103],{"class":147}," SWATCHES",[48,2105,2106],{"class":114},"[:",[48,2108,605],{"class":147},[48,2110,479],{"class":114},[48,2112,1111],{"class":110},[48,2114,2103],{"class":147},[48,2116,2117],{"class":114},"[",[48,2119,163],{"class":147},[48,2121,98],{"class":114},[48,2123,1414],{"class":147},[48,2125,2126],{"class":114},"]:\n",[48,2128,2129,2131,2133],{"class":50,"line":428},[48,2130,1524],{"class":114},[48,2132,302],{"class":110},[48,2134,2135],{"class":114}," resolve(palette, theme, tint)\n",[48,2137,2138,2140,2142,2144,2146,2148,2151,2153,2155,2157,2159,2161,2163,2165,2167,2169,2171,2173,2175,2177,2179,2181,2183,2185,2187,2189,2191,2193,2195,2197,2199,2201,2203,2205,2207,2209,2212,2214,2216,2218,2220],{"class":50,"line":472},[48,2139,1589],{"class":147},[48,2141,345],{"class":114},[48,2143,1221],{"class":110},[48,2145,1049],{"class":58},[48,2147,1226],{"class":1035},[48,2149,2150],{"class":114},"label",[48,2152,1608],{"class":110},[48,2154,1244],{"class":1035},[48,2156,1636],{"class":58},[48,2158,1226],{"class":1035},[48,2160,1641],{"class":114},[48,2162,1244],{"class":1035},[48,2164,1646],{"class":1035},[48,2166,1649],{"class":58},[48,2168,1652],{"class":110},[48,2170,1540],{"class":147},[48,2172,345],{"class":114},[48,2174,1105],{"class":147},[48,2176,1547],{"class":114},[48,2178,1111],{"class":110},[48,2180,1114],{"class":147},[48,2182,1117],{"class":114},[48,2184,1120],{"class":147},[48,2186,1123],{"class":114},[48,2188,333],{"class":110},[48,2190,1135],{"class":114},[48,2192,339],{"class":110},[48,2194,1140],{"class":114},[48,2196,574],{"class":147},[48,2198,160],{"class":114},[48,2200,379],{"class":147},[48,2202,160],{"class":114},[48,2204,163],{"class":147},[48,2206,1578],{"class":114},[48,2208,339],{"class":110},[48,2210,2211],{"class":114}," rendered ",[48,2213,1658],{"class":110},[48,2215,1661],{"class":58},[48,2217,1244],{"class":1035},[48,2219,1049],{"class":58},[48,2221,469],{"class":114},[10,2223,2224,2225,2228,2229,807,2232,2235,2236,2238,2239,807,2241,2243],{},"The cells were not touched — only two values in the theme changed — yet Accent 1 swatches now render in the brand blue ",[45,2226,2227],{},"#1F6FEB"," and its tints, ",[45,2230,2231],{},"#79A9F3",[45,2233,2234],{},"#D2E2FB",", and Accent 2 in the brand gold. The regular expression replaces only ",[45,2237,1676],{}," values inside the named accent elements, which is where Office themes store accents; system colours such as ",[45,2240,587],{},[45,2242,577],{}," use a different element and are left alone. That is the practical case for theme colours in generated reports: build every template with theme colours, and a new client's palette, or a refreshed corporate identity, is a change to one XML part rather than a hunt through every style in the code. Colours that must never change — red for losses, say — are the ones to set as fixed RGB.",[655,2245,664,2250,664,2253,664,2256,664,2259,664,2265,664,2276,664,2282,664,2285,664,2290,664,2295,664,2300,664,2307,664,2312,664,2315,664,2319,664,2322,664,2325,664,2332,664,2337,664,2340,664,2344,664,2347,664,2350,664,2357,664,2363,664,2367],{"viewBox":2246,"role":658,"ariaLabelledBy":2247,"xmlns":662,"style":663},"4 0 752 142",[2248,2249],"tc-rebrand-t","tc-rebrand-d",[666,2251,2252],{"id":2248},"Rebranding through the theme",[670,2254,2255],{"id":2249},"Cells store theme references such as Accent 1 lighter 40 percent. Editing the accent colours in the workbook's theme XML and saving makes every theme-coloured fill, font and border recompute from the new accents, without changing any cell.",[674,2257],{"x":163,"y":574,"width":676,"height":2258,"fill":678},"142",[674,2260],{"x":681,"y":682,"width":2261,"height":2262,"rx":2263,"fill":711,"stroke":685,"style":2264},"153.0","110.5","12","stroke-width:2px",[2266,2267,2269],"g",{"transform":2268},"translate(84.5,37.8) scale(1.000)",[2270,2271],"path",{"d":2272,"fill":2273,"stroke":2274,"style":2275},"M3 5h18v14H3z M3 10h18 M3 15h18 M9 5v14","none","var(--text,#172033)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[695,2277,2281],{"x":2278,"y":2279,"style":2280},"96.5","84.2","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Cells",[695,2283,2284],{"x":2278,"y":1721,"style":722},"theme=4, tint=0.4",[50,2286],{"x1":2287,"y1":2288,"x2":2289,"y2":2288,"stroke":692,"style":2264},"178.0","71.2","200.0",[2291,2292],"polygon",{"points":2293,"fill":2294},"204.0,71.2 195.0,76.2 195.0,66.2","#5b5cf0",[674,2296],{"x":2297,"y":682,"width":2261,"height":2262,"rx":2263,"fill":2298,"stroke":2299,"style":2264},"209.0","#fdefd8","var(--gold,#b4740a)",[2266,2301,2303],{"transform":2302},"translate(273.5,37.8) scale(1.000)",[2270,2304],{"d":2305,"fill":2273,"stroke":2306,"style":2275},"M9 8l-4 4 4 4 M15 8l4 4-4 4","var(--gold-ink,#7a4e06)",[695,2308,2311],{"x":2309,"y":2279,"style":2310},"285.5","font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Theme XML",[695,2313,2314],{"x":2309,"y":1721,"style":722},"accent1 = 4F81BD",[50,2316],{"x1":2317,"y1":2288,"x2":2318,"y2":2288,"stroke":692,"style":2264},"367.0","389.0",[2291,2320],{"points":2321,"fill":2294},"393.0,71.2 384.0,76.2 384.0,66.2",[674,2323],{"x":2324,"y":682,"width":2261,"height":2262,"rx":2263,"fill":691,"stroke":692,"style":2264},"398.0",[2266,2326,2328],{"transform":2327},"translate(462.5,37.8) scale(1.000)",[2270,2329],{"d":2330,"fill":2273,"stroke":2331,"style":2275},"M20 11a8 8 0 1 0-2.3 5.7 M20 4v7h-7","var(--brand-strong,#4338ca)",[695,2333,2336],{"x":2334,"y":2279,"style":2335},"474.5","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Edit accents",[695,2338,2339],{"x":2334,"y":1721,"style":722},"accent1 = 1F6FEB",[50,2341],{"x1":2342,"y1":2288,"x2":2343,"y2":2288,"stroke":692,"style":2264},"556.0","578.0",[2291,2345],{"points":2346,"fill":2294},"582.0,71.2 573.0,76.2 573.0,66.2",[674,2348],{"x":2349,"y":682,"width":2261,"height":2262,"rx":2263,"fill":1758,"stroke":1759,"style":2264},"587.0",[2266,2351,2353],{"transform":2352},"translate(651.5,29.0) scale(1.000)",[2270,2354],{"d":2355,"fill":2273,"stroke":2356,"style":2275},"M5 12.5l4.5 4.5L19 7","var(--teal-ink,#0b6157)",[695,2358,2362],{"x":2359,"y":2360,"style":2361},"663.5","75.5","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Every swatch",[695,2364,2366],{"x":2359,"y":2365,"style":2361},"93.0","follows",[695,2368,2370],{"x":2359,"y":2369,"style":722},"112.5","no cell changed",[33,2372,2374],{"id":2373},"common-pitfalls","Common pitfalls",[2376,2377,2378,2389,2395,2404,2410],"ul",{},[2379,2380,2381,2388],"li",{},[2382,2383,2384,2387],"strong",{},[45,2385,2386],{},"theme=0"," for text."," Index 0 is Background 1; text is index 1.",[2379,2390,2391,2394],{},[2382,2392,2393],{},"Expecting RGB when reading colours."," Theme colours come back as an index and a tint.",[2379,2396,2397,2400,2401,2403],{},[2382,2398,2399],{},"Hard-coding a palette."," Read it from ",[45,2402,1672],{}," so the resolver follows the workbook.",[2379,2405,2406,2409],{},[2382,2407,2408],{},"Tints outside −1 to 1."," Keep them in range; Excel's picker uses ±0.25 to 0.8.",[2379,2411,2412,2415],{},[2382,2413,2414],{},"Theme colours for meaning."," Use fixed RGB where the colour carries meaning, such as red for losses.",[33,2417,2419],{"id":2418},"conclusion","Conclusion",[10,2421,2422,2423,2426,2427,2429],{},"Use ",[45,2424,2425],{},"Color(theme=..., tint=...)"," in openpyxl to colour cells with the workbook theme — index 0 and 1 for Background 1 and Text 1, 2 and 3 for Background 2 and Text 2, 4 to 9 for the accents — and positive or negative tints to lighten or darken. Resolve theme colours to hex by reading ",[45,2428,1672],{}," and applying Excel's luminance tint, check them against a render, and rebrand a whole workbook by changing the accent colours in its theme.",[33,2431,2433],{"id":2432},"frequently-asked-questions","Frequently asked questions",[10,2435,2436,2439],{},[2382,2437,2438],{},"How do I use a theme colour in openpyxl?","\nPass Color(theme=4) for Accent 1, or another theme index, wherever openpyxl accepts a colour, such as PatternFill(fgColor=...) or Font(color=...).",[10,2441,2442,2445],{},[2382,2443,2444],{},"How do I make a lighter or darker theme colour in openpyxl?","\nAdd a tint between -1 and 1: Color(theme=4, tint=0.4) is Accent 1 lighter 40%, and tint=-0.25 is darker 25%.",[10,2447,2448,2451],{},[2382,2449,2450],{},"Why is my openpyxl theme text colour white?","\nTheme index 0 is Background 1, usually white; Text 1 is index 1.",[10,2453,2454,2457],{},[2382,2455,2456],{},"How do I get the RGB value of a theme colour?","\nRead the theme XML from wb.loaded_theme, take the colour for the index, and apply the tint to its luminance in HLS colour space.",[33,2459,2461],{"id":2460},"related","Related",[2376,2463,2464,2470,2477,2484],{},[2379,2465,2466,2467,2469],{},"Up one level: ",[27,2468,30],{"href":29}," — openpyxl essentials.",[2379,2471,2472,2476],{},[27,2473,2475],{"href":2474},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl\u002Fapply-a-reusable-style-theme-across-an-excel-report\u002F","Apply a Reusable Style Theme Across an Excel Report"," — named styles built on these colours.",[2379,2478,2479,2483],{},[27,2480,2482],{"href":2481},"\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"," — reading colours back.",[2379,2485,2486,2490],{},[27,2487,2489],{"href":2488},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl\u002Fapply-built-in-cell-styles-like-good-bad-and-neutral\u002F","Apply Built-In Cell Styles Like Good, Bad and Neutral"," — Excel's ready-made styles.",[2492,2493,2494],"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 .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .s_b0D, html code.shiki .s_b0D{--shiki-default:#22863A;--shiki-default-font-weight:bold;--shiki-dark:#72F088;--shiki-dark-font-weight:bold}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}",{"title":43,"searchDepth":124,"depth":124,"links":2496},[2497,2498,2499,2500,2501,2502,2503,2504,2505],{"id":35,"depth":124,"text":36},{"id":82,"depth":124,"text":83},{"id":535,"depth":124,"text":536},{"id":800,"depth":124,"text":801},{"id":1781,"depth":124,"text":1782},{"id":2373,"depth":124,"text":2374},{"id":2418,"depth":124,"text":2419},{"id":2432,"depth":124,"text":2433},{"id":2460,"depth":124,"text":2461},"2026-10-02","Colour Excel cells with the workbook theme instead of fixed RGB values using openpyxl: theme colour indexes and the swapped order of the first four, tints that lighten and darken like Excel's palette, resolving theme colours to hex for checks and HTML, and rebranding a whole report by changing the theme's accent colours — verified against a rendered copy.","md",[2510,2512,2514,2516],{"q":2438,"a":2511},"Pass Color(theme=4) for Accent 1, or another theme index, wherever openpyxl accepts a colour, such as PatternFill(fgColor=...) or Font(color=...).",{"q":2444,"a":2513},"Add a tint between -1 and 1: Color(theme=4, tint=0.4) is Accent 1 lighter 40%, and tint=-0.25 is darker 25%.",{"q":2450,"a":2515},"Theme index 0 is Background 1, usually white; Text 1 is index 1.",{"q":2456,"a":2517},"Read the theme XML from wb.loaded_theme, take the colour for the index, and apply the tint to its luminance in HLS colour space.","theme colours and tints openpyxl",null,{"breadcrumb":2521},[2522,2524,2527],{"name":2523,"item":1126},"Home",{"name":2525,"item":2526},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":30,"item":29},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fuse-theme-colours-and-tints-in-openpyxl",{"title":5,"description":2530},"Theme-based colours in openpyxl: Color(theme=, tint=), the index order (lt1, dk1, lt2, dk2, accents), tint maths, resolving to RGB from the theme XML, and swapping accent colours to rebrand a workbook, checked against a render.","use-theme-colours-and-tints-in-openpyxl","getting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fuse-theme-colours-and-tints-in-openpyxl\u002Findex",[2534,2535],"openpyxl","cell-styling","how-to","mLCsBpCwfdTAa4rXGWT_gzi8FjwDhRMte684LDUIIS0",[2539,2543],{"title":2540,"path":2541,"stem":2542,"children":-1},"Unmerge Cells and Fill the Values with openpyxl","\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Funmerge-cells-and-fill-values-with-openpyxl","getting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Funmerge-cells-and-fill-values-with-openpyxl\u002Findex",{"title":2544,"path":2545,"stem":2546,"children":-1},"What openpyxl Loses When You Save an Existing Workbook","\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fwhat-openpyxl-loses-when-saving-an-existing-workbook","getting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fwhat-openpyxl-loses-when-saving-an-existing-workbook\u002Findex",[2548,2550],{"path":2549,"title":2525},"\u002Fgetting-started-with-python-excel-automation",{"path":2551,"title":30},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation",[],[2554,2558,2561,2564,2567,2570],{"path":2555,"title":2556,"kind":2557},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl","Applying Conditional Formatting With openpyxl","guide",{"path":2559,"title":2560,"kind":2536},"\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":2562,"title":2563,"kind":2536},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fadd-icon-sets-with-openpyxl","Add Icon Sets to Excel Cells with openpyxl",{"path":2565,"title":2566,"kind":2536},"\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":2568,"title":2569,"kind":2536},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fcontrol-conditional-formatting-rule-order-and-stop-if-true","Control Conditional Formatting Rule Order and Stop If True",{"path":2571,"title":2572,"kind":2536},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fhighlight-an-entire-row-based-on-a-cell-value-with-openpyxl","Highlight an Entire Row Based on a Cell Value with openpyxl",1790972072253]