Use a single stylesheet instead of per widget instance style sheets

Better for DOM mutation performance
This commit is contained in:
Kovid Goyal 2016-06-14 10:28:47 +05:30
parent bdaa808886
commit ec625be3b4
9 changed files with 124 additions and 86 deletions

View File

@ -11,7 +11,7 @@ from elementmaker import E
from gettext import gettext as _ from gettext import gettext as _
from book_list.globals import get_boss from book_list.globals import get_boss
from modals import error_dialog from modals import error_dialog
from widgets import create_spinner, create_button from widgets import create_spinner, create_button, add_extra_css
from date import format_date from date import format_date
from utils import fmt_sidx from utils import fmt_sidx
@ -121,6 +121,7 @@ def render_metadata(mi, interface_data, table, field_list=None):
def process_formats(field, fm, name, val): def process_formats(field, fm, name, val):
table.appendChild(E.tr(E.td(name + ':'), E.td())) table.appendChild(E.tr(E.td(name + ':'), E.td()))
# TODO: Change this to have the read/download options in a popup
for fmt in val: for fmt in val:
td = table.lastChild.lastChild td = table.lastChild.lastChild
td.appendChild(E.span(fmt, style='white-space: nowrap')) td.appendChild(E.span(fmt, style='white-space: nowrap'))
@ -255,6 +256,16 @@ def render_metadata(mi, interface_data, table, field_list=None):
if i is 0: if i is 0:
div.style.marginTop = '2ex' div.style.marginTop = '2ex'
CLASS_NAME = 'book-details-panel'
add_extra_css(def():
sel = '.' + CLASS_NAME + ' '
style = build_rule(sel + 'table.metadata td:first-of-type', font_weight='bold', padding_right='1em', white_space='nowrap')
style += build_rule(sel + 'table.metadata a[href]', color='blue')
style += build_rule(sel + 'table.metadata a[href]:hover', color='red')
style += build_rule(sel + 'table.metadata a[href]:active', color='red', transform='scale(1.5)')
return style
)
class BookDetailsPanel: class BookDetailsPanel:
@ -262,13 +273,8 @@ class BookDetailsPanel:
nonlocal bd_counter nonlocal bd_counter
bd_counter += 1 bd_counter += 1
self.container_id = 'book-details-panel-' + bd_counter self.container_id = 'book-details-panel-' + bd_counter
style = build_rule('#' + self.container_id + ' table.metadata td:first-of-type', font_weight='bold', padding_right='1em', white_space='nowrap')
style += build_rule('#' + self.container_id + ' table.metadata a[href]', color='blue')
style += build_rule('#' + self.container_id + ' table.metadata a[href]:hover', color='red')
style += build_rule('#' + self.container_id + ' table.metadata a[href]:active', color='red', transform='scale(1.5)')
div = E.div( div = E.div(
id=self.container_id, style='display:none', id=self.container_id, style='display:none', class_=CLASS_NAME,
E.style(style, type='text/css'),
E.div(), E.div(),
) )
book_list_container.appendChild(div) book_list_container.appendChild(div)

View File

@ -4,10 +4,23 @@ from __python__ import hash_literals
from dom import build_rule, svgicon from dom import build_rule, svgicon
from elementmaker import E from elementmaker import E
from widgets import add_extra_css
from book_list.theme import get_font_size, get_color from book_list.theme import get_font_size, get_color
iv_counter = 0 iv_counter = 0
CLASS_NAME = 'generic-items-list'
add_extra_css(def():
sel = '.' + CLASS_NAME + ' '
style = ''
style += build_rule(sel + 'li', padding='1em', border_bottom='solid 1px ' + get_color('window-foreground'), border_top='solid 1px ' + get_color('window-background'), cursor='pointer', list_style='none')
style += build_rule(sel + '.item-title', font_size=get_font_size('item-list-title'))
style += build_rule(sel + ' .item-subtitle', font_size=get_font_size('item-list-subtitle'), font_style='italic')
style += build_rule(sel + ' li:hover', color=get_color('list-hover-foreground'), background_color=get_color('list-hover-background'), border_top_color=get_color('list-hover-foreground'))
style += build_rule(sel + ' li:active', transform='scale(1, 1.5)')
return style
)
class ItemsView: class ItemsView:
@ -15,17 +28,8 @@ class ItemsView:
nonlocal iv_counter nonlocal iv_counter
iv_counter += 1 iv_counter += 1
self.container_id = 'items-view-' + iv_counter self.container_id = 'items-view-' + iv_counter
style = ''
cid = '#' + self.container_id
style += build_rule(cid + ' li', padding='1em', border_bottom='solid 1px ' + get_color('window-foreground'), border_top='solid 1px ' + get_color('window-background'), cursor='pointer', list_style='none')
style += build_rule(cid + ' .item-title', font_size=get_font_size('item-list-title'))
style += build_rule(cid + ' .item-subtitle', font_size=get_font_size('item-list-subtitle'), font_style='italic')
style += build_rule(cid + ' li:hover', color=get_color('list-hover-foreground'), background_color=get_color('list-hover-background'), border_top_color=get_color('list-hover-foreground'))
style += build_rule(cid + ' li:active', transform='scale(1, 1.5)')
self.base_style = style
div = E.div( div = E.div(
id=self.container_id, style='display:none', id=self.container_id, style='display:none', class_=CLASS_NAME,
E.style(style, type='text/css')
) )
book_list_container.appendChild(div) book_list_container.appendChild(div)

View File

@ -175,10 +175,8 @@ class PrefsPanel:
nonlocal iv_counter nonlocal iv_counter
pp_counter += 1 pp_counter += 1
self.container_id = 'prefs-panel-' + pp_counter self.container_id = 'prefs-panel-' + pp_counter
style = ''
div = E.div( div = E.div(
id=self.container_id, style='display:none', id=self.container_id, style='display:none',
E.style(style, type='text/css')
) )
book_list_container.appendChild(div) book_list_container.appendChild(div)
self.widgets = [] self.widgets = []

View File

@ -6,12 +6,33 @@ from ajax import ajax
from dom import clear, set_css, build_rule, svgicon from dom import clear, set_css, build_rule, svgicon
from elementmaker import E from elementmaker import E
from gettext import gettext as _ from gettext import gettext as _
from widgets import create_button, create_spinner, Breadcrumbs from widgets import create_button, create_spinner, Breadcrumbs, add_extra_css
from modals import show_modal from modals import show_modal
from book_list.globals import get_boss, get_session_data from book_list.globals import get_boss, get_session_data
from book_list.theme import get_color, get_font_size from book_list.theme import get_color, get_font_size
sp_counter = 0 sp_counter = 0
CLASS_NAME = 'book-search-panel'
add_extra_css(def():
sel = '.' + CLASS_NAME + ' '
style = build_rule(sel + ' div.tag-name:hover', color=get_color('list-hover-foreground'), background_color=get_color('list-hover-background'))
style += build_rule(sel + ' div.tag-menu:hover', color=get_color('list-hover-foreground'), background_color=get_color('list-hover-background'))
style += build_rule(sel + ' div.tag-name:active', transform='scale(1.5)')
style += build_rule(sel + ' div.tag-menu:active', transform='scale(2)')
# search items list
style += build_rule(sel + ' ul.search-items', margin_top='1ex', list_style_type='none', text_align='left')
style += build_rule(sel + ' ul.search-items > li', display='inline-block', cursor='pointer', background_color=get_color('window-background2'), border_radius='10px', padding='0.5ex', margin_right='1em')
style += build_rule(sel + ' ul.search-items > li:hover', color='red')
style += build_rule(sel + ' ul.search-items > li:active', transform='scale(1.5)')
# Actions popup
style += build_rule('#modal-container ul.tb-action-list > li:hover', color=get_color('list-hover-foreground'), background_color=get_color('list-hover-background'))
style += build_rule('#modal-container ul.tb-action-list > li:active', color='red', color=get_color('list-hover-foreground'), background_color=get_color('list-hover-background'))
return style
)
class SearchPanel: class SearchPanel:
@ -21,20 +42,9 @@ class SearchPanel:
self.container_id = 'search-panel-' + sp_counter self.container_id = 'search-panel-' + sp_counter
self.interface_data = interface_data self.interface_data = interface_data
self.tag_path = [] self.tag_path = []
style = build_rule('#' + self.container_id + ' div.tag-name:hover', color=get_color('list-hover-foreground'), background_color=get_color('list-hover-background'))
style += build_rule('#' + self.container_id + ' div.tag-menu:hover', color=get_color('list-hover-foreground'), background_color=get_color('list-hover-background'))
style += build_rule('#' + self.container_id + ' div.tag-name:active', transform='scale(1.5)')
style += build_rule('#' + self.container_id + ' div.tag-menu:active', transform='scale(2)')
# search items list
style += build_rule('#' + self.container_id + ' ul.search-items', margin_top='1ex', list_style_type='none', text_align='left')
style += build_rule('#' + self.container_id + ' ul.search-items > li', display='inline-block', cursor='pointer', background_color=get_color('window-background2'), border_radius='10px', padding='0.5ex', margin_right='1em')
style += build_rule('#' + self.container_id + ' ul.search-items > li:hover', color='red')
style += build_rule('#' + self.container_id + ' ul.search-items > li:active', transform='scale(1.5)')
div = E.div( div = E.div(
id=self.container_id, style='display:none', id=self.container_id, style='display:none', class_=CLASS_NAME,
E.style(style, type='text/css'),
E.div(style="text-align:center; padding:1ex 1em; border-bottom: solid 1px currentColor; margin-bottom: 0.5ex"), # search input container E.div(style="text-align:center; padding:1ex 1em; border-bottom: solid 1px currentColor; margin-bottom: 0.5ex"), # search input container
E.div( E.div(
E.div(), E.div(),
@ -45,7 +55,7 @@ class SearchPanel:
book_list_container.appendChild(div) book_list_container.appendChild(div)
# Build search input # Build search input
search_container = div.firstChild.nextSibling search_container = div.firstChild
search_button = create_button(_('Search'), icon='search', action=self.execute_search.bind(self), tooltip=_('Do the search')) search_button = create_button(_('Search'), icon='search', action=self.execute_search.bind(self), tooltip=_('Do the search'))
search_container.appendChild(E.div(style="display: flex; width: 100%;", search_container.appendChild(E.div(style="display: flex; width: 100%;",
E.input( E.input(
@ -304,11 +314,8 @@ class SearchPanel:
if items.length: if items.length:
suffix = ' [' + items.join(' ') + ']' suffix = ' [' + items.join(' ') + ']'
style = build_rule('#modal-container ul.tb-action-list > li:hover', color=get_color('list-hover-foreground'), background_color=get_color('list-hover-background'))
style += build_rule('#modal-container ul.tb-action-list > li:active', color='red', color=get_color('list-hover-foreground'), background_color=get_color('list-hover-background'))
title = E.h2( title = E.h2(
style='display:flex; align-items: center; border-bottom: solid 1px currentColor; font-weight:bold; font-size:' + get_font_size('title'), style='display:flex; align-items: center; border-bottom: solid 1px currentColor; font-weight:bold; font-size:' + get_font_size('title'),
E.style(style),
E.img(src=self.icon_for_node(node), style='height:2ex'), E.img(src=self.icon_for_node(node), style='height:2ex'),
E.span('\xa0' + name + suffix) E.span('\xa0' + name + suffix)
) )
@ -465,7 +472,7 @@ class SearchPanel:
@property @property
def search_items_container(self): def search_items_container(self):
return self.container.firstChild.nextSibling.lastChild return self.container.firstChild.lastChild
@property @property
def is_visible(self): def is_visible(self):

View File

@ -6,41 +6,42 @@ from book_list.theme import get_color, get_font_size
from dom import set_css, clear, create_keyframes, build_rule, svgicon from dom import set_css, clear, create_keyframes, build_rule, svgicon
from elementmaker import E from elementmaker import E
from gettext import gettext as _ from gettext import gettext as _
from widgets import add_extra_css
bar_counter = 0 bar_counter = 0
CLASS_NAME = 'main-top-bar'
class TopBar:
SPACING = '0.75em' SPACING = '0.75em'
VSPACING = '0.5ex' VSPACING = '0.5ex'
THROBBER_NAME = 'top-bar-throbber'
add_extra_css(def():
sel = '.' + CLASS_NAME + ' '
style = ''
style += create_keyframes(THROBBER_NAME, 'from { transform: scale(1); } 50% { transform: scale(0.5); } to { transform: scale(1); }')
style += build_rule(sel + 'a', display='inline-block', vertical_align='middle', overflow='hidden', cursor='pointer', color=get_color('bar-foreground'), background='none', padding_top=VSPACING, padding_bottom=VSPACING)
style += build_rule(sel + 'a:hover', transform='scale(1.5)')
style += build_rule(sel + 'a:active', transform='scale(2)')
style += build_rule(sel + 'a:focus', outline='none')
style += build_rule(sel + 'a.top-bar-title:hover', transform='scale(1)', color=get_color('bar-highlight'), font_style='italic')
style += build_rule(sel + 'a.top-bar-title:active', transform='scale(1)', color=get_color('bar-highlight'), font_style='italic')
return style
)
class TopBar:
def __init__(self, book_list_container): def __init__(self, book_list_container):
nonlocal bar_counter nonlocal bar_counter
bar_counter += 1 bar_counter += 1
self.current_left_data = {} self.current_left_data = {}
self.bar_id, self.dummy_bar_id = 'top-bar-' + bar_counter, 'dummy-top-bar-' + bar_counter self.bar_id, self.dummy_bar_id = 'top-bar-' + bar_counter, 'dummy-top-bar-' + bar_counter
self.throbber_name = self.bar_id + '-throbber'
style = create_keyframes(self.throbber_name, 'from { transform: scale(1); } 50% { transform: scale(0.5); } to { transform: scale(1); }')
for sel in (self.dummy_bar_id, self.bar_id):
sel = '#' + sel + ' a'
style += build_rule(
sel, display='inline-block', vertical_align='middle', overflow='hidden', cursor='pointer',
color=get_color('bar-foreground'), background='none', padding_top=self.VSPACING, padding_bottom=self.VSPACING
)
style += build_rule(sel + ':hover', transform='scale(1.5)')
style += build_rule(sel + ':active', transform='scale(2)')
style += build_rule(sel + ':focus', outline='none')
style += build_rule(sel + '.top-bar-title:hover', transform='scale(1)', color=get_color('bar-highlight'), font_style='italic')
style += build_rule(sel + '.top-bar-title:active', transform='scale(1)', color=get_color('bar-highlight'), font_style='italic')
for bid in self.dummy_bar_id, self.bar_id: for bid in self.dummy_bar_id, self.bar_id:
bar = E.div( bar = E.div(
id=bid, id=bid, class_=CLASS_NAME,
E.div(style="white-space:nowrap; overflow:hidden; text-overflow: ellipsis; padding-left: 0.5em;"), E.div(style="white-space:nowrap; overflow:hidden; text-overflow: ellipsis; padding-left: 0.5em;"),
E.div(style="white-space:nowrap; text-align:right; padding-right: 0.5em;") E.div(style="white-space:nowrap; text-align:right; padding-right: 0.5em;")
) )
if bid is self.bar_id: if bid is self.bar_id:
set_css(bar, position='fixed', left='0', top='0', z_index='1') set_css(bar, position='fixed', left='0', top='0', z_index='1')
bar.appendChild(E.style(style, type='text/css'))
set_css(bar, set_css(bar,
width='100%', display='flex', flex_direction='row', flex_wrap='wrap', justify_content='space-between', width='100%', display='flex', flex_direction='row', flex_wrap='wrap', justify_content='space-between',
font_size=get_font_size('title'), user_select='none', font_size=get_font_size('title'), user_select='none',
@ -73,12 +74,12 @@ class TopBar:
title_elem = 'a' if callable(title_action) else 'span' title_elem = 'a' if callable(title_action) else 'span'
left.appendChild(E.a(title=tooltip, svgicon(icon_name))) left.appendChild(E.a(title=tooltip, svgicon(icon_name)))
left.appendChild(E(title_elem, title, title=title_tooltip, class_='top-bar-title', left.appendChild(E(title_elem, title, title=title_tooltip, class_='top-bar-title',
style='margin-left: {0}; font-weight: bold; padding-top: {1}; padding-bottom: {1}; vertical-align: middle'.format(self.SPACING, self.VSPACING))) style='margin-left: {0}; font-weight: bold; padding-top: {1}; padding-bottom: {1}; vertical-align: middle'.format(SPACING, VSPACING)))
if bar is self.bar: if bar is self.bar:
a = left.firstChild a = left.firstChild
if icon_name is 'heart': if icon_name is 'heart':
set_css(a, set_css(a,
animation_name=self.throbber_name, animation_duration='1s', animation_timing_function='ease-in-out', animation_name=THROBBER_NAME, animation_duration='1s', animation_timing_function='ease-in-out',
animation_iteration_count='5', animation_play_state='running' if run_animation else 'paused' animation_iteration_count='5', animation_play_state='running' if run_animation else 'paused'
) )
set_css(a.firstChild, color=get_color('heart')) set_css(a.firstChild, color=get_color('heart'))
@ -101,7 +102,7 @@ class TopBar:
for bar in self.bar, self.dummy_bar: for bar in self.bar, self.dummy_bar:
right = bar.firstChild.nextSibling right = bar.firstChild.nextSibling
right.appendChild(E.a( right.appendChild(E.a(
style="margin-left: " + self.SPACING, style="margin-left: " + SPACING,
title=tooltip, svgicon(icon_name), title=tooltip, svgicon(icon_name),
)) ))
right.lastChild.setAttribute('id', ('top' if bar is self.bar else 'dummy') + '-bar-icon-' + icon_name) right.lastChild.setAttribute('id', ('top' if bar is self.bar else 'dummy') + '-bar-icon-' + icon_name)

View File

@ -4,10 +4,11 @@ from __python__ import hash_literals
import traceback import traceback
from ajax import ajax_send from ajax import ajax_send
from dom import set_css, build_rule, clear from dom import set_css, clear, build_rule
from elementmaker import E from elementmaker import E
from gettext import gettext as _ from gettext import gettext as _
from modals import error_dialog, ajax_progress_dialog from modals import error_dialog, ajax_progress_dialog
from widgets import add_extra_css
from book_list.globals import get_session_data, get_boss from book_list.globals import get_session_data, get_boss
from widgets import create_button, create_spinner from widgets import create_button, create_spinner
@ -17,6 +18,16 @@ THUMBNAIL_MAX_HEIGHT = 400
bv_counter = 0 bv_counter = 0
CLASS_NAME = 'books-main-list'
def widget_css():
ans = ''
sel = '.' + CLASS_NAME + ' '
ans += build_rule(sel + '.cover_grid > div:hover', transform='scale(1.2)')
ans += build_rule(sel + '.cover_grid > div:active', transform='scale(2.0)')
return ans
add_extra_css(widget_css)
class BooksView: class BooksView:
def __init__(self, interface_data, book_list_container): def __init__(self, interface_data, book_list_container):
@ -29,11 +40,8 @@ class BooksView:
# We have to apply the transform on the containing div not the img because of a bug in WebKit # We have to apply the transform on the containing div not the img because of a bug in WebKit
# that causes img aspect ratios to be messed up on window resize if the transform is specified # that causes img aspect ratios to be messed up on window resize if the transform is specified
# on the img itself # on the img itself
style = build_rule('#' + self.container_id + ' .cover_grid > div:hover', transform='scale(1.2)')
style += build_rule('#' + self.container_id + ' .cover_grid > div:active', transform='scale(2.0)')
div = E.div( div = E.div(
id=self.container_id, style='display:block', id=self.container_id, style='display:block', class_=CLASS_NAME,
E.style(style),
E.div(), E.div(),
E.div() E.div()
) )

View File

@ -8,10 +8,23 @@ from dom import set_css, clear, build_rule, svgicon
from gettext import gettext as _ from gettext import gettext as _
from book_list.theme import get_color, get_font_size from book_list.theme import get_color, get_font_size
from popups import MODAL_Z_INDEX from popups import MODAL_Z_INDEX
from widgets import add_extra_css
modal_container = None modal_container = None
modal_count = 0 modal_count = 0
add_extra_css(def():
style = build_rule(
'#modal-container > div > a:hover',
color=get_color('dialog-foreground') + ' !important',
background_color=get_color('dialog-background') + ' !important'
)
style += build_rule(
'#modal-container a.dialog-simple-link:hover', color='red !important'
)
return style
)
class Modal: class Modal:
def __init__(self, create_func, on_close, show_close): def __init__(self, create_func, on_close, show_close):
@ -27,16 +40,6 @@ class ModalContainer:
E.div( # popup E.div( # popup
E.div(), # content area E.div(), # content area
E.a(svgicon('close'), title=_('Close')) E.a(svgicon('close'), title=_('Close'))
),
E.style(
build_rule(
'#modal-container > div > a:hover',
color=get_color('dialog-foreground') + ' !important',
background_color=get_color('dialog-background') + ' !important'
) +
build_rule(
'#modal-container a.dialog-simple-link:hover', color='red !important'
)
) )
) )
document.body.appendChild(div) document.body.appendChild(div)

View File

@ -6,7 +6,7 @@ from dom import clear, set_css, element, svgicon, build_rule
from elementmaker import E from elementmaker import E
from book_list.theme import get_color from book_list.theme import get_color
from book_list.globals import get_boss from book_list.globals import get_boss
from widgets import create_spinner, create_button from widgets import create_spinner, create_button, add_extra_css
from gettext import gettext as _ from gettext import gettext as _
from read_book.toc import create_toc_panel from read_book.toc import create_toc_panel
@ -93,7 +93,21 @@ class DeleteBook: # {{{
self.overlay.hide_current_panel() self.overlay.hide_current_panel()
# }}} # }}}
class MainOverlay: # {{{ # MainOverlay {{{
MAIN_OVERLAY_TS_CLASS = 'read-book-main-overlay-top-section'
add_extra_css(def():
sel = '.' + MAIN_OVERLAY_TS_CLASS + ' '
style = build_rule(sel + '.button-row > div:hover', transform='scale(1.5)')
style += build_rule(sel + '.button-row > div:active', transform='scale(2)')
style += build_rule(sel + '.item-list > li', padding='1ex 1rem', border_bottom='solid 1px currentColor', cursor='pointer')
style += build_rule(sel + '.item-list > li:hover', color=get_color('window-background'), background_color=get_color('window-foreground'))
style += build_rule(sel + '.item-list > li:active', transform='scaleY(2)')
return style
)
class MainOverlay:
def __init__(self, overlay): def __init__(self, overlay):
self.overlay = overlay self.overlay = overlay
@ -107,7 +121,7 @@ class MainOverlay: # {{{
def show(self, container): def show(self, container):
self.container_id = container.getAttribute('id') self.container_id = container.getAttribute('id')
container.appendChild(set_css(E.div( # top section container.appendChild(set_css(E.div(class_=MAIN_OVERLAY_TS_CLASS, # top section
onclick=def (evt):evt.stopPropagation();, onclick=def (evt):evt.stopPropagation();,
set_css(E.div( # top row set_css(E.div( # top row
@ -157,16 +171,6 @@ class MainOverlay: # {{{
add_button('cogs', _('Configure the book reader')) add_button('cogs', _('Configure the book reader'))
add_button() add_button()
sel = '#{} .button-row '.format(self.container_id)
sel2 = '#{} .item-list'.format(self.container_id)
container.appendChild(E.style(
build_rule(sel + '> div:hover', transform='scale(1.5)'),
build_rule(sel + '> div:active', transform='scale(2)'),
build_rule(sel2 + '> li', padding='1ex 1rem', border_bottom='solid 1px currentColor', cursor='pointer'),
build_rule(sel2 + '> li:hover', color=get_color('window-background'), background_color=get_color('window-foreground')),
build_rule(sel2 + '> li:active', transform='scaleY(2)'),
))
def update_time(self): def update_time(self):
element(self.container_id, '[data-time]').textContent = self.date_formatter.format(Date()) element(self.container_id, '[data-time]').textContent = self.date_formatter.format(Date())

View File

@ -202,10 +202,17 @@ def scroll_tree_item_into_view(item):
# }}} # }}}
extra_css = []
def add_extra_css(func):
extra_css.push(func)
def get_widget_css(): def get_widget_css():
ans = 'a, button:focus { outline: none }; a, button::-moz-focus-inner { border: 0 }\n' ans = 'a, button:focus { outline: none }; a, button::-moz-focus-inner { border: 0 }\n'
ans += '.simple-link { cursor: pointer } .simple-link:hover { color: red } .simple-link:active { transform: scale(1.5) }\n' ans += '.simple-link { cursor: pointer } .simple-link:hover { color: red } .simple-link:active { transform: scale(1.5) }\n'
ans += create_button.style ans += create_button.style
ans += create_spinner.style ans += create_spinner.style
ans += Breadcrumbs.STYLE_RULES ans += Breadcrumbs.STYLE_RULES
for func in extra_css:
ans += '\n' + func()
return ans return ans