colors holds primary plus named colors: 14 are predefined (red, teal and so on), and you can add your own, such as moss: '#2e7d32'. Each gets a .color-moss class that retints the controls inside an element, plus .text-color-, .bg-color- and .border-color- helpers.
app.setColors() writes all of this into one <style> element prepended to <head>. Its :root rule holds both themes' variables, .dark holds the dark scheme, and two rules map --f7-theme-color, which the components read, to --f7-ios-primary under .ios or --f7-md-primary under .md. app.setColorTheme() rebuilds it:
from playwright.sync_api import sync_playwright
PROBE = '''() => { const css = document.head.firstElementChild.textContent;
const s = getComputedStyle(document.documentElement);
return [css.length + ' chars', ...['--f7-theme-color', '--f7-ios-primary', '--f7-md-primary']
.map((n) => n + ' ' + s.getPropertyValue(n).trim())].join(' | '); }'''
with sync_playwright() as p:
page = p.chromium.launch(channel='chrome').new_page()
page.goto('http://localhost:5294/?theme=md')
page.wait_for_function('window.app && app.initialized')
print(page.evaluate(PROBE))
page.evaluate("app.setColorTheme('#2e7d32')")
print(page.evaluate(PROBE))49804 chars | --f7-theme-color #8a5033 | --f7-ios-primary #b3541e | --f7-md-primary #8a5033 49802 chars | --f7-theme-color #42683e | --f7-ios-primary #2e7d32 | --f7-md-primary #42683e
Each extra named color adds about 3 KB (moss took it to 52,859 characters). Because the element comes first, your own rules of equal specificity win, so you can tune one role in app.css, such as .md { --f7-md-surface-2: #fbe9e1; }. Give each override a .md.dark twin: in a test, that rule also beat the generated .dark rule, and the tab bar stayed light in dark mode.