#!/usr/bin/env python "Accessibility CSS Generator, (c) Silas S. Brown 2006-24. Version 0.9938" # Works on either Python 2 or Python 3 # Website: http://ssb22.user.srcf.net/css/ # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # CHANGES # ------- # If you want to compare this code to old versions, the old # versions are being kept in the E-GuideDog SVN repository on # http://svn.code.sf.net/p/e-guidedog/code/ssb22/css-generator # and on GitHub at https://github.com/ssb22/css-generator # and on GitLab at https://gitlab.com/ssb22/css-generator # and on BitBucket https://bitbucket.org/ssb22/css-generator # and at https://gitlab.developers.cam.ac.uk/ssb22/css-generator # and in China: https://gitee.com/ssb22/css-generator # as of v0.9782. Versions prior to that were not kept, but # some were captured by Internet Archive at # https://web.archive.org/web/*/http://people.pwf.cam.ac.uk/ssb22/css/css-generate.py # CONFIGURATION # ------------- # The default configuration can be changed here, or if you # prefer you can copy and paste all of this to a separate # file called css_generate_config.py and edit it there # (which should make it easier to update the code # independently of your configuration). # Where to put the output CSS files: default is current directory, # but you might want to put them in a subdirectory, or set # an absolute path outputDir = "." try: True # backward compatibility with Python 2.1: except: exec("True,False=1,0") # (syntax error in Python 3) # Whether to write an HTML menu to standard output as well # (see my CSS page for example) outHTML = True # if putting this on the Web, include in .htaccess: # AddType text/css css # SetEnvIf Request_URI "\.css$" requested_css=css # Header add Content-Disposition "Attachment" env=requested_css # Which pixel sizes to generate. # Size 0 means "unchanged" - it will disable the size # changes, and the layout changes that are meant for large # sizes. This is for people who need only colour changes, # or for whom the browser's built-in zoom is sufficient # (e.g. on a large display and/or complex layouts that # aren't very-well dealt with by our layout changes). pixel_sizes_to_generate = [0,18,20,25,30,35,40,45,50,60,75,100] # Which pixel size to use with the "chop" and related debug options: chop_pixel_size = 20 # Which colour schemes to generate. The format of this is # [("description","filename-prefix",{...}),...] # - see the existing ones for examples. # If you have many of these, you might want to read them # in from separate configuration files instead of listing # them here. In that case, set colour_schemes_to_generate # to a string containing a wildcard, # e.g. colour_schemes_to_generate = "my_config_dir/*.conf" # in which case each file in my_config_dir/*.conf should # contain one or more entries in the same [(...),...] format # WARNING: this will be passed to Python's eval() function # which can execute any command, so don't allow an # untrusted third party to write these *.conf files. colour_schemes_to_generate = [ ("yellow on black","", {"text":"yellow","background":"black", "translucent_background_compromise":"rgba(0,0,0,0.5)", "headings":"#8080FF","link":"#00FF00", "hover-bkg":"#0000C0","visited":"#00FFFF", "bold":"#FFFF80","italic":"white", "coloured":"pink", # used for text inside any FONT COLOR= markup "button-bkg":"#600040", "select-bkg":"#600060", "reset-bkg":"#400060", "form_disabled":"#404040", # GrayText requires CSS 2.1 "selection-bkg":"#006080", # (if supported by the browser. BEWARE: Some browsers, e.g. Safari 6, will NOT display this exact colour, but a computed medium mid-way between it and the unselected background; you should therefore ensure that other backgrounds (e.g. highlight) are discernable against those 'computed medium' colours as well) "highlight-bkg":"#300030", # (misc non-selection highlights in site-specific hacks) "image_transparency_compromise":"#808000" # non-black and non-white background for transparent images, so at least stand a chance of seeing transparent imgs that were meant for white bkg (or black bkg) }), ("green on black","green", {"text":"#00FF00","background":"black", "translucent_background_compromise":"rgba(0,0,0,0.5)", "headings":"#40C080","link":"#008AFF", "hover-bkg":"#400000","visited":"#00FFFF", "bold":"#80FF80","italic":"white", "button-bkg":"#600040", "select-bkg":"#600060", "coloured":"#80C040", "reset-bkg":"#400060", "form_disabled":"#404040", "selection-bkg":"#4000c0", "highlight-bkg":"#003050", "image_transparency_compromise":"#808000" }), ("white on black","WonB", {"text":"white","background":"black", "translucent_background_compromise":"rgba(0,0,0,0.5)", "headings":"#40C090","link":"#0080FF", "hover-bkg":"#400000","visited":"#00FFFF", "bold":"yellow","italic":"#FFFF80", "button-bkg":"#600040", "select-bkg":"#600060", "coloured":"#FFFF40", "reset-bkg":"#400060", "form_disabled":"#404040", "selection-bkg":"#4080c0", "highlight-bkg":"#003050", "image_transparency_compromise":"#808080" }), ("soft greys","soft", # c.f. Nightshift etc; thanks to Liviu Andronic for testing {"text":"#C0C0C0","background":"#383838", "translucent_background_compromise":"rgba(56,56,56,0.5)", "alt-backgrounds":["#333333","#2E2E2E"], # optional "headings":"#40C090","link":"#BDB76B", "hover-bkg":"#453436","visited":"#B6AA7B", "bold":"#CD853F","italic":"#EFEFCF", "button-bkg":"#553030", "select-bkg":"#603440", "coloured":"#E0E040", "reset-bkg":"#303055", "form_disabled":"#555753", "selection-bkg":"#5f5f5f", "highlight-bkg":"#2e3050", "focusOutlineStyle":"solid #006080", "image_opacity":0.8, "image_transparency_compromise":"#2e3436" }), ("black on linen","BonL", # LyX's background colour is "linen", 240/230/220 {"text":"black","background":"#faf0e6", "translucent_background_compromise":"rgba(250,240,230,0.5)", "headings":"#404040","link":"#0000FF", "hover-bkg":"#80C0C0","visited":"#008020", "bold":"black","italic":"#400000", "button-bkg":"#608040", "select-bkg":"#608060", "coloured":"#001040", "reset-bkg":"#408060", "form_disabled":"#A0A0A0", "highlight-bkg":"#FFFFE6", "image_transparency_compromise":"#faf0e6" }), ("black on white","BonW", # cld call this "black on bright white" (as opposed to "black on linen white") but that causes the list to take up more width {"text":"black","background":"white", "translucent_background_compromise":"rgba(255,255,255,0.5)", "headings":"#404040","link":"#0000FF", "hover-bkg":"#80C0C0","visited":"#008020", "bold":"black","italic":"#400000", "button-bkg":"#608040", "select-bkg":"#608060", "coloured":"#001040", "reset-bkg":"#408060", "form_disabled":"#A0A0A0", "highlight-bkg":"#FFFF80", "image_transparency_compromise":"white" }), ] # Some other options you might want to change: separate_adjacent_links_at_size_0 = False # sometimes interferes with layouts separate_adjacent_links_at_other_sizes = True # Fonts: (cjk_fonts is listed first so it can be used in both serif_fonts and sans_serif_fonts) cjk_fonts = "Lantinghei SC, AppleGothic" # AppleGothic must be listed or Korean is broken on Mac OS 10.7 # Lantinghei SC was introduced to OS X in 10.9, which is handy because the previously-good STSong font (which was the system default for Simplified Chinese) broke on 10.9: it renders badly with antialiasing turned off at 20px, e.g. missing the horizontal stroke on U+95E8. So we set Lantinghei SC for 10.9 but fall back to STSong on 10.8/10.7/etc (I don't think we need to explicitly say STSong, and there are advantages in not doing so, e.g. the TODO below about :lang(ja) is not relevant for pre-10.9 systems) # TODO: for :lang(ko) and :lang(ja) we had better put AppleGothic and a Japanese font like YuGothic first (before Lantinghei) - see below re U+8D77, U+95E8 etc. Pity can't read the system preferences for pages that don't set a CJK value of LANG. This :lang exception needs to be done separately for every element that has a font-family, to avoid corrupting headings etc. # Other Mac CJK fonts to be aware of: MingLiU prefers full 'Traditional' forms of characters where Trad/Simp has same Unicode value (e.g. U+8D77 'qi3' has an extra vertical stroke making the 'ji' component look more like a 'si'); renders OK on Mac OS 10.9 at 20px without antialias, but might not always be present (the ttf is installed to /Library/Fonts/Microsoft by MS Office and is not present on machines without MS Office). Arial Unicode MS (present on both 10.7 and 10.9) has some issues with baselines not lining up e.g. in the word 'zhen1li3' U+771F U+7406; it prefers Simplifed Chinese forms (e.g. U+8D77 uses 'ji', and U+95E8 is the Chinese rather than the Japanese simplification). GB18030 Bitmap (NISC18030) might work at 16px, 32px etc, but scales badly to other sizes. "Hei" has irregular stroke widths in 10.9 20px no-antialias, but otherwise OK serif_fonts = "Times New Roman, times, utopia, /* charter, */ "+cjk_fonts+", serif" # TNR is listed first for the benefit of broken Xft systems that need the MS fonts to make them look OK. Shouldn't have any effect on other systems. sans_serif_fonts = "helvetica, arial, verdana, "+cjk_fonts+", sans-serif" # (TODO: do we want different cjk_fonts here?) pinyin_fonts = "FreeSerif, Lucida Sans Unicode, Times New Roman, DejaVu Sans, serif" # try to get clear tone marks (but DejaVu Sans must be low priority because it results in disappearing text on some buggy Safari versions under Mac OS 10.7) browser_is_Firefox_73 = False # set this to True ONLY if you are using Firefox 73, to work around bug 1616243. Do NOT set it to True on Firefox 74+ as it will make checkboxes unreadable. # ---- End of options (but read on for debugging) ---- # DEBUGGING BY BINARY CHOP: If a complex stylesheet exhibits # a behaviour you weren't expecting (maybe due to a browser # bug but maybe not) then it might not be obvious how to fix # it. This program has a debug mode that helps you do it by # binary chop. Run like this: # python css-generate.py chop # which will generate a single debug .css file with half # the attributes disabled. Try that debug .css; if the # problem persisted, then do # python css-generate.py chop 1 # or if the problem did not persist, do # python css-generate.py chop 0 # and then try again. Then add another 1 or 0 depending on # whether or not the problem persisted the next time, e.g. # python css-generate.py chop 01 # Hopefully it will narrow down the problem to one thing. # You will see which one that is from the debug messages # it prints on standard output (these will appear instead of # the HTML index of stylesheets that is usually generated). chop_extra_verification = True # If True, we'll take an extra step to verify each chop result (by checking we get the opposite in the inverse set) before further subdivisions # For more desperate debugging cases, you can try: # python css-generate.py desperate-debug # which will generate a large number of stylesheets, each # adding one more rule and not combining. (Note that some # rules at the beginning and end of the stylesheet will be # there unconditionally though.) # TODO consider forcing "display" to its normal value (not "none") # if some sites are going to use stock scripts that switch them on and # off every few seconds inadvertently making the rest of the page dance around # ---- End of debugging info, code follows ---- try: from css_generate_config import * except ImportError: pass try: any except: # backward compatibility with Python 2.1: def any(l): for i in l: if i: return True try: dict except: def dict(l): r = {} for k,v in l: r[k] = v return r try: set except: def set(l): return l try: sorted except: def sorted(l,cmpFunc=None): r = l[:] r.sort(cmpFunc) return r try: reduce # Python 2 except: # Python 3 from functools import reduce, cmp_to_key _builtin_sorted = sorted def sorted(l,theCmp=None): if theCmp: return _builtin_sorted(l,key=cmp_to_key(theCmp)) else: return _builtin_sorted(l) def cmp(a,b): return (a>b)-(a tspan'] # used within svg, sometimes for nothing more than effect (unfortunately there doesn't seem to be a way of ensuring the containing svg is displayed large enough, but truncation is better than having the text go underneath other elements) mostElements += html5Elements mostElements += ['location'] # site-specific hack for lib.cam.ac.uk css={} ; printOverride = {} webkitScreenOverride = {} ; geckoScreenOverride = {} ; msieScreenOverride = {} webkitGeckoScreenOverride = {} ; webkitMsieScreenOverride = {} ; geckoMsieScreenOverride = {} for e in mostElements+rubyElements: css[e]=defaultStyle.copy() printOverride[e] = {"color":"black","background":"white"}.copy() printOverride[e]["*font-size"] = "12pt" # TODO: option? geckoMsieScreenOverride[e] = {"*column-count":"1"} # not Webkit (PageUp/PageDown bug in Chrome57 etc) # but there are some exceptions: for e in rubyElements: del css[e]["*text-align"] if not pixelSize: # Size = unchanged, much-reduced layout changes. But a LOT of "modern" sites assume they can stack elements (DIV, A, etc) with some transparency. This can result in obscuring text if we have solid colour backgrounds. But we don't want it completely transparent (unless we can confirm that's correct for a site-specific hack) as we probably won't be able to catch all UI elements as exceptions. for e in css.keys(): if 'background' in css[e] and not e in ['html','body']: css[e]['background'] = colour["translucent_background_compromise"] # Also do this for Firefox's PDF viewer: css["div.pdfViewer div.page > div.canvasWrapper + div.textLayer"]={"opacity":"1"} del css['svg']['*font-size'] # doesn't make sense to override, as it's subject to the resize of the whole SVG (usually an enlargement) del printOverride['svg']['*font-size'] for e in ['text','text > tspan']: # may help with SVG Lilypond output del css[e]["*font-size"],css[e]["*font-family"],css[e]["*font-weight"],css[e]["*font-variant"] del printOverride[e]["*font-size"] for k in list(css["img"].keys()): if k.startswith("background"): del css["img"][k] # e.g. WhatsApp emoji uses a single image with positioning (and we want this to work if size=unchanged) for k in list(printOverride["img"].keys()): if k.startswith("background"): del printOverride["img"][k] css["rt:lang(cmn-hans),rt:lang(zh)"]={"*font-family":pinyin_fonts} del css["rt"]["*padding"] # some sites omit space between ruby elements and make up for it by setting padding on the rt elements: let that through del css["ruby"]["*padding"];del css["rb"]["*padding"] # might as well do this too for w in ["*width","*height"]: del css["svg"][w] # 'auto' is very often wrong for svg, and some browsers' understanding of specificity results in our viewBox overrides not working if auto is set here for t in ["textarea","html","body","input"]: css[t]["*overflow"] = "auto" # 'html' is there for IE7. But Firefox needs it to be 'visible'. See hack at end. # TODO previously excluded 'body' (and deleted 'body' overflow 'visible') # as it somehow disables keyboard scrolling in IE7, however do need to # set 'body' to "auto' to work around CouchDB's "overflow:none" in both # 'html' and 'body' (at least in Firefox); need to find an IE7 to re-test # (if broken, consider re-instating the delete and move body:auto to the # non-IE7 override hack at end) # del css["body"]["*overflow"] # Do not set both "body" and "html" in IE7 - it disables keyboard-only scrolling! del css["input"]["*overflow"] # for IE 6 and possibly 8 overprinting too-long input type="text" with a horizontal scrollbar for e in [ # remove height:auto and width:auto from: "object","embed", # should not be forced to 'auto' as that can sometimes break Flash applications (when the Flash application is actually useful) "img", # can break on some versions of IE (is added back in for other browsers below) "input", # can result in 0 on some versions of Gecko (Firefox 60-62 are affected, at least with checkboxes and radio buttons) ]: del css[e]["*width"], css[e]["*height"] css[exclude_ie_below_9+"img"]={"*width":"auto","*height":"auto"} # but we can at least add img back on non-IE browsers (TODO: which versions of IE were affected?) - we DO need to specify this, to cope with sites that do silly things like set image height to something e+7 pixels and expect layering to compensate css["textarea"]["*width"]="100%" # not "auto", as that can cause Firefox to sometimes indent the textarea's contents off-screen css["frame"]={} for e in ["frame","iframe"]: css[e]["*overflow"]="auto" # to override 'scrolling=no' which can go wrong in large print (but this override doesn't always work) css["sup"]["*vertical-align"] = "super" # in case authors try to do it with font size instead css["sub"]["*vertical-align"] = "sub" css["marquee"]["*-moz-binding"]="none" # don't scroll marquee elements css["marquee"]["*display"]="block" css["center"]["*text-align"] = "center" for s in ['s','strike']: css[s]["*text-decoration"]="line-through" # TODO: not sure if really want this for the 's' alias of 'strike', since some sites e.g. http://www.elgin.free-online.co.uk/qp_intro.htm (2007-10) use CSS to override its presentation into something other than strikeout css['span[style="text-decoration:line-through"],span[style="text-decoration:line-through;"]']={"*text-decoration":"line-through"} # used on some sites # Margin exceptions: css["body"]["*margin"]="1ex %.1fpx 1ex %.1fpx" % (pixelSize*5/18.0,pixelSize*5/18.0) # keep away from window borders for i in "p,multicol,listing,plaintext,xmp,pre".split(","): css[i]["*margin"]="1em 0" css["li > p:first-child"]={"*margin-top":"0"} # I'm not sure P is intended to go inside LI like this (especially when there's only one paragraph), but GitHub does it as of 2017 so I suppose we should try to cope (positioning of the circle is still a bit sub-optimal though) listStuff="ul,dir,menu,dl,li".split(",") # not ol, leave that as margin 0px - see ol > li below for l in listStuff: css[l]["*margin"]="0 1em 0 %.1fpx" % (pixelSize*10/18.0) listStuff.remove("li") for l in listStuff: for l2 in listStuff: css[l+" "+l2]={"*margin":"0px"} css["ol > li"] = {"*list-style-position":"inside","*margin":"0px","*padding-left":"1em","*text-indent":"-1em"} # helps when numbers get very large css["blockquote"]["*margin"]="1em 4em" css["blockquote[type=cite]"]={"*margin":"1em 0px","*padding":"1em 0px 0px 0px"} css["dd"]["*margin"]="0em 2em" for t in ["th","td"]: css[t]["*padding"]="%.1fpx" % (pixelSize/18.0,) css[t+'[align^="right"]'] = {"*text-align":"right"} # Don't say white-space normal on user input elements or pre # Galeon 1.25: we also have to exclude "body" and "div" for some reason # TODO: is it REALLY a good idea to leave 'div' on this list? for e in "pre,input,textarea,body,div".split(","): del css[e]["*white-space"] for e in "font,code,tt,span,b".split(","): css["pre "+e]={"*white-space":"inherit"} # some mailing lists etc have "font" within "pre", and some sites have "code" within "pre" # Monospaced elements for t in "pre,code,tt,kbd,var".split(","): css[t]["*font-family"]="monospace" # and 'samp' let's have sans-serif for t in "samp".split(","): css[t]["*font-family"]=sans_serif_fonts css["spacer"]={"*display":"none"} # no point in keeping the spacers now we've changed the layout so much css["a"]["*display"] = "inline" # some sites override it to block, which might have worked OK in their CSS's context but it's not so good in ours # max-width (if supported, i.e. not IE6) can reduce left/right scrolling - # if one line's linebox needs to expand (e.g. due to a long word), then we # don't want to expand the whole block (e.g. the table cell) and all its # other line boxes. BUT: if a max-width'd TD (or even a DIV etc) does # have to overflow in a big way (e.g. due to map images), and there is # something else to the right (e.g. nested tables with rowspans), # overprinting can result, unless also using 'overflow:auto' which can # create too many nested or offscreen scrollbars. No way to say "use this # width for internal formatting, then expand to overflows for external # bounds" or "use this width if you are a leaf node with mostly text" or # whatever. Only alternative for now is to say "none" and have to # horizontally scroll. (Even "p" is not safe to set - consider a # table containing a p containing another table that overflows.) # (If set td max-width to "-moz-available" later in the stylesheet, will # cause Firefox 3 to do less overprinting than it would have done while # Firefox 2 takes the previous one and does a better job incorrectly, but # then there are still some instances of overprinting regardless of # version etc., especially on mapping sites) # (Note: On Firefox 2 (not 3), max-width works in a nice way, see Bugzilla # bug report at https://bugzilla.mozilla.org/show_bug.cgi?id=452840 - so # you may want to uncomment the following if you are particularly on Firefox 2.) # for e in mostElements: css[e]["*max-width"]=("%.1fpx" % min(1200,pixelSize*470/18)) # Links stuff - must be before bold/italic colour overrides: for linkInside in ",font,big,small,basefont,br,b,i,u,em,cite,strong,abbr,span,div,code,tt,samp,kbd,var,acronym,h1,h2,h3,h4,h5,h6".split(",")+rubyElements: for lType in [":link",":visited","[onclick]", ".button", # used by some JS applications ]: css["a"+lType+" "+linkInside]={"color":colour["link"],"text-decoration":"underline","cursor":"pointer"} printOverride["a"+lType+" "+linkInside]={"color":"#000080"} # printing: links in blue might be useful for sending PDFs to others, but make it a dark blue so still readable if printed in black and white (don't try to ensure page is ALWAYS black and white: that can't be done w/out suppressing images. User needs to suppress colour at print time. But ensure legible choice of shading when that happens.) css["a"+lType+":hover "+linkInside]={"background":colour["hover-bkg"]} css["a"+lType+":active "+linkInside]={"color":"red","text-decoration":"underline","cursor":"pointer"} if linkInside in ["b","i","em","u","strong"] and not css[linkInside]["color"]==colour["text"]: css["a"+lType+" "+linkInside]["color"]=css[linkInside]["color"] css["a:visited "+linkInside]["color"]=colour["visited"] # set cursor:pointer for links and ANYTHING inside them (including images etc). The above cursor:auto should theoretically do the right thing anyway, but it seems that some versions of Firefox need help. for linkInside in mostElements+rubyElements: for lType in [":link",":visited","[onclick]"]: key="a"+lType+" "+linkInside css.setdefault(key,{})["cursor"]="pointer" if not linkInside in rubyElements: css[key]["*display"]="inline" # some sites have 'div' or do JS things with 'span'... # Italic and bold: for i in "i,em,cite,address,dfn,u".split(","): css[i+" span"]={ "*font-family":sans_serif_fonts, "color":colour["italic"]} printOverride[i+" span"]={"color":"black"} css[i].update(css[i+" span"]) for i in "b,strong".split(","): css[i+" span,"+i+" kbd"]={ "*font-weight":"bold", "color":colour["bold"]} printOverride[i+" span,"+i+" kbd"]={"color":"black"} css[i].update(css[i+" span,"+i+" kbd"]) css["acronym"]["color"]=colour["bold"] css["abbr"]["color"]=colour["bold"] css["ins"]["color"]=colour["italic"] css["del"]["color"]=colour["italic"] css["del"]["text-decoration"]="line-through" # Some browsers might start styling abbr by default but not acronym. Some older browsers might understand acronym title= but not abbr title=, so some sites might try to use acronym= for backward compatibility, but given that this must be for nonessential information anyway (as many simpler browsers don't support either) it probably makes sense to prefer abbr now (if it might be displayed by default on a greater number of modern browsers) unless the webmaster wants to emulate the browser in CSS. css["acronym"]["border-bottom"]="1px dotted" css["abbr"]["border-bottom"]="1px dotted" # Headings stuff (must be after italic/bold): indent = 0 for h in range(6): el="h%d" % (h+1) css[el]["*font-family"]=sans_serif_fonts size = (largestHeadingSize-h*(largestHeadingSize-smallestHeadingSize)/(6-1.0)) css[el]["*font-size"]="%.1fpx" % size css[el]["*font-weight"]="bold" # ensure 'h1 strong' etc inherits family (but not necessarily colour): for i in ['strong','em','i','b']: css[el+" "+i]=css[el].copy() css[el+" "+i]["color"]=css[i]["color"] printOverride[el+" "+i]={"color":"black"} # now for heading colour: css[el]["color"]=colour["headings"] printOverride[el]={"color":"black"} # ensure links in headings inherit size and family: css[el+" center"]=css[el].copy() # rather than the default for 'center' css[el+" a"]=css[el].copy() # because it's usually A NAME (in the case of HREF, the specificity of a:link should be greater) css[el+" abbr"]=css[el].copy() ; del css[el+" abbr"]["*text-decoration"] css[el+" span"]=css[el].copy() css[el+" a b"]=css[el].copy() printOverride[el+" center"]=printOverride[el].copy() printOverride[el+" a"]=printOverride[el].copy() printOverride[el+" abbr"]=printOverride[el].copy() printOverride[el+" span"]=printOverride[el].copy() printOverride[el+" a b"]=printOverride[el].copy() # and now (AFTER the above) set margins on headings if h: indent += size css[el]["*margin"]="0px 0px 0px %.1fpx" % indent # Images and buttons: css["img:not(.emoji)"]={"background":colour["image_transparency_compromise"]} # see WhatsApp exception above # Exception needed for MediaWiki TeX images # (they tend to be transparent but with antialiasing that # assumes the background will be white) css["body.mediawiki img.tex"]={"background":"white"} # (note however it might be possible to set the wiki to # display maths as real TeX or something instead) if not colour["background"]=="white": css["body.mediawiki img.tex"]["border"]="white solid 3px" # to make sure letters near the edge are readable if the rest of the page has a dark background if "image_opacity" in colour.keys(): del css["img"]["*opacity"],css["img"]["filter"] css["img"]["opacity"]="%g" % colour["image_opacity"] css["img"]["filter"]="alpha(opacity=%d)" % int(colour["image_opacity"]*100) # for IE8 and below if colour["image_opacity"]<0.9: css["img:hover"] = css["a:hover img"]={"opacity":"0.9","filter":"alpha(opacity=90)"} css["button"]["background"]=colour["button-bkg"] printButtonBackground = "#e0e0e0" # light grey, TODO: option printOverride["button"]["background"]=printButtonBackground css['div[role="button"]']={"background":colour["button-bkg"]} # for Gmail 2012-07 on "standard" view (rather than "basic HTML" view). "Standard" view might work for people who want the "unchanged" size. printOverride['div[role="button"]']={"background":printButtonBackground} if "alt-backgrounds" in colour.keys(): # override specificity of alt-backgrounds div:nth-child css['html body div[role="button"]'] = css['div[role="button"]'] printOverride['html body div[role="button"]'] = {"background":printButtonBackground} css["input[type=submit]"]={"background":colour["button-bkg"]} css["input[type=button]"]={"background":colour["button-bkg"]} css["input[type=reset]"]={"background":colour["reset-bkg"]} printOverride["input[type=submit]"]={"background":printButtonBackground} printOverride["input[type=button]"]={"background":printButtonBackground} printOverride["input[type=reset]"]={"background":printButtonBackground} for f in ["select","input","textarea","button"]: k = "html "+f+'[disabled]' # must include 'html' so more specific than above (TODO: or :not(:empty) if got enough CSS?) if f=='input': k += ", html "+f+'[readonly]' css[k]={"background":colour["form_disabled"]} printOverride[k]={"background":printButtonBackground} # TODO: or something else? # Separate adjacent links (CSS2+) if (pixelSize and separate_adjacent_links_at_other_sizes) or (not pixelSize and separate_adjacent_links_at_size_0): for l in [":link",":visited","[onclick]"]: css[exclude_ie_below_9+"a"+l+":before"]={"content":'"["',"color":colour["text"],"text-decoration":"none","white-space":"nowrap"} css[exclude_ie_below_9+"a"+l+":after"]={"content":'"]"',"color":colour["text"],"text-decoration":"none","white-space":"nowrap"} # make sure the hover colour includes :before and :after - this is needed if the :before/:after text is changed by site-specific hacks etc (to cope with empty links) css[exclude_ie_below_9+"a"+l+":hover:before"]={"background":colour["hover-bkg"]} css[exclude_ie_below_9+"a"+l+":hover:after"]={"background":colour["hover-bkg"]} printOverride[exclude_ie_below_9+"a"+l+":before"]={"color":"black"} # TODO: option to delete the "[" "]" content also? printOverride[exclude_ie_below_9+"a"+l+":after"]={"color":"black"} # Avoid style overrides from :first-letter, :first-line, # :before and :after in author's CSS. However be careful # which elements you do this because of browser bugs. firstLetterBugs_multiple=[ "input","select","option","textarea","table","colgroup","col","img", # probably best to avoid these "div", # Gecko messes up textarea when enter multiple paragraphs; Safari has text selection visibility problem see below "svg","text","text > tspan","object", # doesn't make sense, and can cause confusion ] firstLetterBugs_geckoOnly=[ # none here for now ] firstLetterBugs_webkitOnly=[ # The following cause text selection visibility problems in Webkit / Safari 5/6 (cannot be worked around with :first-letter::selection) # (added 'caption' 2022: causing selection issues in Safari 15) # (+ Chrome 12 bug - OL/LI:first-letter ends up being default size rather than css size; harmless if have default size set similarly anyway) "main", # Safari 17.3 "label","address","p","ol","ul","li","pre","code","body","html","h1","h2","h3","h4","h5","h6","form","th","tr","td","dl","dt","dd","b","blockquote","section","header","footer","center","article","span","aside","figure","caption","figcaption","time","em" ] firstLetterBugs_msie=["a"] assert not(any([(x in firstLetterBugs_geckoOnly or x in firstLetterBugs_webkitOnly or x in firstLetterBugs_msie) for x in firstLetterBugs_multiple]) or any([(x in firstLetterBugs_webkitOnly or x in firstLetterBugs_msie) for x in firstLetterBugs_geckoOnly]) or any([(x in firstLetterBugs_msie) for x in firstLetterBugs_webkitOnly])), "Error: firstLetterBugs item in more than one category" firstLineBugs=[ "div", # on firefox 2 causes some google iframes to occlude page content "svg","text","text > tspan","object", # doesn't make sense, and can cause confusion "input","select","option","textarea","table","colgroup","col","img", "td","th", # causes problems on Firefox 2 if there's a form inside "a", # causes problems in IE "span", # sometimes causes crashes in Opera 12 "li", # sometimes causes crashes in Opera 12 (note this might be sacrificing some control, if someone does try a li:first-line override) # To be safe, could add other inline-etc tags mentioned in mostElements: "label","nobr","tr","ol","ul","abbr","acronym","dfn","em","strong","code","samp","kbd","var","b","i","u","small","s","big","strike","tt","font","cite","q","sub","sup","blink","button","command","dir","embed","object","fieldset","iframe","marquee","basefont","bdi","canvas","time", # TODO: other :first-line, :first-letter and :hover overrides can still crash Opera 12 on some sites. Have suggested in index.html that the user replaces :first with :girst and :hover with :gover when installing one of these CSS files to Opera. ] inheritDic={"color":"inherit","background":"inherit","*letter-spacing":"inherit","*font-size":"inherit","*font-family":"inherit"} # (NB must say inherit, because consider things like p:first-line / A HREF... - the first-line may have higher specificity. # If IE7 seems to be getting first lines and first letters wrong, check that Ignore Document Colours is NOT set - "document" can include parts of the CSS. and try toggling high-contrast mode twice.) for e in mostElements: if not e in firstLetterBugs_multiple: if e in firstLetterBugs_geckoOnly: dictToAddTo = webkitScreenOverride # or webkitMsieScreenOverride, but would have to test MSIE versions elif e in firstLetterBugs_webkitOnly: dictToAddTo = geckoScreenOverride # or geckoMsieScreenOverride, but would have to test MSIE versions elif e in firstLetterBugs_msie: dictToAddTo = webkitGeckoScreenOverride else: dictToAddTo = css dictToAddTo[e+":first-letter"]=inheritDic.copy() if not e in firstLineBugs: css[e+":first-line"]=inheritDic.copy() for i in map(lambda x,e=e:exclude_ie_below_9+e+x,[":before",":after"]): css[i]=defaultStyle.copy() if e=="img": del css[i]["background"] else: css[i]["background"]="transparent" # essential for 0.css where the pseudo-element might be repositioned with different z-index; not supported by IE below 9 but neither are pseudo-elements in general (we're on exclude_ie_below_9 anyway) for mp in ["*margin","*padding"]: if not css.get(e,{}).get(mp,"")==css[i][mp]: del css[i][mp] # as not sure how browsers would treat a different margin/padding in :before/:after. But DO keep these settings for the 0px elements, because we DON'T want sites overriding this and causing overprinting. # and also do this for no specific element: for i in map(lambda x:exclude_ie_below_9+x,[":before",":after"]): css[i]=defaultStyle.copy() # (especially margin and padding) css[i]["background"]="transparent" # see above # inheritDic may also be useful for common child elements of links, highlights etc: for e in rubyElements: css[e].update(inheritDic) del printOverride[e] # CSS 2+ markup for viewing XML+CSS pages that don't use HTML. Not perfect but should be better than nothing. xmlKey=":root:not(HTML):not(page):not(svg), :root:not(HTML):not(page):not(svg) :not(:empty)" # Careful not to use the universal selector, because it can mess up Mozilla's UI. # :not(page) is an important addition for recent versions of Firefox whose Preference pages start with 'page' (can be rendered invisible if apply whole of defaultStyle to it). css["page:root *"]={"background-color":colour["background"]} # to normalise recent-Firefox preferences pages (without this, some parts do and some don't; result can look too stark). Tested in Firefox 45.4. css[xmlKey]=defaultStyle.copy() del css[xmlKey]["*text-decoration"] # because this CSS won't be able to put it back in for links (since it doesn't know which elements ARE links in arbitrary XML) # Exception to above for Mozilla scrollbars: css[":root:not(HTML):not(page):not(svg) slider:not(:empty)"]={"background":"#301090"} # and Firefox icons: (TODO: this may need re-testing on affected versions of Firefox; needed to add :not(svg) or get link-coloured parts in object-embedded SVG files in Safari) css[":root:not(HTML):not(svg) *"]={"-moz-context-properties":"fill,fill-opacity","fill":colour["link"],"fill-opacity":"1"} checkbox_scale = int(pixelSize/16) for iType in ["checkbox","radio"]: iKey = "input[type="+iType+"]" if checkbox_scale > 1: css[iKey]={"transform":"scale(%d,%d)" % (checkbox_scale,checkbox_scale),"margin":"%dpx"%(checkbox_scale*6)} # margin not padding (browser problems) else: css[iKey]={} css[iKey]['-webkit-appearance']=iType if browser_is_Firefox_73: css[iKey]['-webkit-appearance'] += ' !important; -moz-appearance: none' if pixelSize: # In many versions of firefox, a

with an