From 4a9d4b5e092dc8d743262111fd9625171aece4a2 Mon Sep 17 00:00:00 2001 From: "Davide P. Cervone" Date: Sun, 19 Oct 2025 15:34:49 -0400 Subject: [PATCH 1/2] Add color handling for dark mode, and refactor CSS definitions for clarity --- ts/css_util.ts | 459 ++++++++++++++++++++++++++++--------------------- 1 file changed, 265 insertions(+), 194 deletions(-) diff --git a/ts/css_util.ts b/ts/css_util.ts index 9457c1b..0738e33 100644 --- a/ts/css_util.ts +++ b/ts/css_util.ts @@ -31,224 +31,295 @@ function makeClass_(name: string): string { return '.' + (HtmlClasses[name] || name); } +/** + * @param def An array of css properties and their values. + * @returns The css definition within braces. + */ +function cssDef_(def: string[]) { + return '{\n ' + def.join(';\n ') + ';\n}'; +} + +/* const INFO_STYLES: { [id: string]: string } = {}; -INFO_STYLES[makeClass_('INFOCLOSE')] = '{' + ' top:.2em; right:.2em;' + '}'; -INFO_STYLES[makeClass_('INFOCONTENT')] = - '{' + - ' overflow:auto; text-align:left; font-size:80%;' + - ' padding:.4em .6em; border:1px inset; margin:1em 0px;' + - ' max-height:20em; max-width:30em; background-color:#EEEEEE;' + - ' white-space:normal;' + - '}'; -INFO_STYLES[makeClass_('INFO') + makeClass_('MOUSEPOST')] = - '{' + 'outline:none;' + '}'; -INFO_STYLES[makeClass_('INFO')] = - '{' + - ' position:fixed; left:50%; width:auto; text-align:center;' + - ' border:3px outset; padding:1em 2em; background-color:#DDDDDD;' + - ' color:black;' + - ' cursor:default; font-family:message-box; font-size:120%;' + - ' font-style:normal; text-indent:0; text-transform:none;' + - ' line-height:normal; letter-spacing:normal; word-spacing:normal;' + - ' word-wrap:normal; white-space:nowrap; float:none; z-index:201;' + - ' border-radius: 15px; /* Opera 10.5 and IE9 */' + - ' -webkit-border-radius:15px; /* Safari and Chrome */' + - ' -moz-border-radius:15px; /* Firefox */' + - ' -khtml-border-radius:15px; /* Konqueror */' + - ' box-shadow:0px 10px 20px #808080; /* Opera 10.5 and IE9 */' + - ' -webkit-box-shadow:0px 10px 20px #808080; /* Safari 3 & Chrome */' + - ' -moz-box-shadow:0px 10px 20px #808080; /* Forefox 3.5 */' + - ' -khtml-box-shadow:0px 10px 20px #808080; /* Konqueror */' + - ' filter:progid:DXImageTransform.Microsoft.dropshadow(OffX=2,' + - ' OffY=2, Color="gray", Positive="true"); /* IE */' + - '}'; +INFO_STYLES[makeClass_('INFOCLOSE')] = cssDef_([ + 'top: 0.2em', + 'right: 0.2em', +]); +INFO_STYLES[makeClass_('INFOCONTENT')] = cssDef_([ + 'overflow: auto', + 'text-align: left', + 'font-size: 80%', + 'padding: 0.4em 0.6em', + 'border: 1px inset', + 'margin: 1em 0', + 'max-height: 20em', + 'max-width: 30em', + 'background-color: #EEEEEE;', + 'white-space: normal' +]); +INFO_STYLES[makeClass_('INFO') + makeClass_('MOUSEPOST')] = cssDef_([ + 'outline: none', +]); +INFO_STYLES[makeClass_('INFO')] = cssDef_([ + 'position: fixed', + 'left: 50%', + 'width: auto', + 'text-align: center', + 'border: 3px outset', + 'padding: 1em 2em', + 'background-color: #DDDDDD', + 'color: black', + 'cursor: default', + 'font-family: message-box', + 'font-size: 120%', + 'font-style: normal', + 'text-indent: 0', + 'text-transform: none', + 'line-height: normal', + 'letter-spacing: normal', + 'word-spacing: normal', + 'word-wrap: normal', + 'white-space: nowrap', + 'float: none', + 'z-index: 1001', + 'border-radius: 15px', + 'box-shadow: 0px 10px 20px #808080', +]); +*/ const MENU_STYLES: { [id: string]: string } = {}; -MENU_STYLES[makeClass_('MENU')] = - '{' + - ' position:absolute;' + - ' background-color:white;' + - ' color:black;' + - ' width:auto; padding:5px 0px;' + - ' border:1px solid #CCCCCC; margin:0; cursor:default;' + - ' font: menu; text-align:left; text-indent:0; text-transform:none;' + - ' line-height:normal; letter-spacing:normal; word-spacing:normal;' + - ' word-wrap:normal; white-space:nowrap; float:none; z-index:201;' + - ' border-radius: 5px; /* Opera 10.5 and IE9 */' + - ' -webkit-border-radius: 5px; /* Safari and Chrome */' + - ' -moz-border-radius: 5px; /* Firefox */' + - ' -khtml-border-radius: 5px; /* Konqueror */' + - ' box-shadow:0px 10px 20px #808080; /* Opera 10.5 and IE9 */' + - ' -webkit-box-shadow:0px 10px 20px #808080; /* Safari 3 & Chrome */' + - ' -moz-box-shadow:0px 10px 20px #808080; /* Forefox 3.5 */' + - ' -khtml-box-shadow:0px 10px 20px #808080; /* Konqueror */' + - '}'; -MENU_STYLES[makeClass_('MENUITEM')] = - '{' + ' padding: 1px 2em;' + ' background:transparent;' + '}'; -MENU_STYLES[makeClass_('MENUARROW')] = - '{' + - ' position:absolute; right:.5em; padding-top:.25em; color:#666666;' + - ' font-family: null; font-size: .75em' + - '}'; -MENU_STYLES[makeClass_('MENUACTIVE') + ' ' + makeClass_('MENUARROW')] = - '{color:white}'; -MENU_STYLES[makeClass_('MENUARROW') + makeClass_('RTL')] = - '{left:.5em; right:auto}'; -MENU_STYLES[makeClass_('MENUCHECK')] = - '{' + ' position:absolute; left:.7em;' + ' font-family: null' + '}'; -MENU_STYLES[makeClass_('MENUCHECK') + makeClass_('RTL')] = - '{ right:.7em; left:auto }'; -MENU_STYLES[makeClass_('MENURADIOCHECK')] = - '{' + ' position:absolute; left: .7em;' + '}'; -MENU_STYLES[makeClass_('MENURADIOCHECK') + makeClass_('RTL')] = - '{' + ' right: .7em; left:auto' + '}'; -MENU_STYLES[makeClass_('MENUINPUTBOX')] = - '{' + - ' padding-left: 1em; right:.5em; color:#666666;' + - ' font-family: null;' + - '}'; -MENU_STYLES[makeClass_('MENUINPUTBOX') + makeClass_('RTL')] = - '{' + ' left: .1em;' + '}'; -MENU_STYLES[makeClass_('MENUCOMBOBOX')] = - '{' + ' left:.1em; padding-bottom:.5em;' + '}'; -MENU_STYLES[makeClass_('MENUSLIDER')] = '{' + ' left: .1em;' + '}'; -MENU_STYLES[makeClass_('SLIDERVALUE')] = - '{' + - ' position:absolute; right:.1em; padding-top:.25em; color:#333333;' + - ' font-size: .75em' + - '}'; -MENU_STYLES[makeClass_('SLIDERBAR')] = - '{' + ' outline: none; background: #d3d3d3' + '}'; -MENU_STYLES[makeClass_('MENULABEL')] = - '{' + ' padding: 1px 2em 3px 1.33em;' + ' font-style:italic' + '}'; -MENU_STYLES[makeClass_('MENURULE')] = - '{' + ' border-top: 1px solid #DDDDDD;' + ' margin: 4px 3px;' + '}'; -MENU_STYLES[makeClass_('MENUDISABLED')] = '{' + ' color:GrayText' + '}'; -MENU_STYLES[makeClass_('MENUACTIVE')] = - '{' + ' background-color: #606872;' + ' color: white;' + '}'; -MENU_STYLES[makeClass_('MENUDISABLED') + ':focus'] = - '{' + ' background-color: #E8E8E8' + '}'; -MENU_STYLES[makeClass_('MENULABEL') + ':focus'] = - '{' + ' background-color: #E8E8E8' + '}'; -MENU_STYLES[makeClass_('CONTEXTMENU') + ':focus'] = - '{' + ' outline:none' + '}'; +MENU_STYLES[makeClass_('MENU')] = cssDef_([ + 'position: absolute', + 'background-color: white', + 'color: black', + 'width: auto', + 'padding: 5px 0px', + 'border: 1px solid #CCCCCC', + 'margin: 0', + 'cursor: default', + 'font: menu', + 'text-align: left', + 'text-indent: 0', + 'text-transform: none', + 'line-height: normal', + 'letter-spacing: normal', + 'word-spacing: normal', + 'word-wrap: normal', + 'white-space: nowrap', + 'float: none', + 'z-index: 1001', + 'border-radius: 5px', + 'box-shadow: 0px 10px 20px #808080', +]); +MENU_STYLES[makeClass_('MENUITEM')] = cssDef_([ + 'padding: 1px 2em', + 'background: transparent', +]); +MENU_STYLES[makeClass_('MENUARROW')] = cssDef_([ + 'position: absolute', + 'right: 0.5em', + 'padding-top: 0.25em', + 'color: #666666', + 'font-family: null', + 'font-size: 0.75em', +]); +MENU_STYLES[makeClass_('MENUACTIVE') + ' ' + makeClass_('MENUARROW')] = cssDef_([ + 'color: white', +]); +MENU_STYLES[makeClass_('MENUARROW') + makeClass_('RTL')] = cssDef_([ + 'left: 0.5em', + 'right: auto', +]); +MENU_STYLES[makeClass_('MENUCHECK')] = cssDef_([ + 'position: absolute', + 'left: 0.7em', + 'font-family: null', +]); +MENU_STYLES[makeClass_('MENUCHECK') + makeClass_('RTL')] = cssDef_([ + 'right: 0.7em', + 'left: auto', +]); +MENU_STYLES[makeClass_('MENURADIOCHECK')] = cssDef_([ + 'position: absolute', + 'left: 0.7em', +]); +MENU_STYLES[makeClass_('MENURADIOCHECK') + makeClass_('RTL')] = cssDef_([ + 'right: 0.7em', + 'left: auto', +]); +MENU_STYLES[makeClass_('MENUINPUTBOX')] = cssDef_([ + 'padding-left: 1em', + 'right: 0.5em', + 'color: #666666', + 'font-family: null', +]); +MENU_STYLES[makeClass_('MENUINPUTBOX') + makeClass_('RTL')] = cssDef_([ + 'left: 0.1em', +]); +MENU_STYLES[makeClass_('MENUCOMBOBOX')] = cssDef_([ + 'left: 0.1em', + 'padding-bottom: 0.5em', +]); +MENU_STYLES[makeClass_('MENUSLIDER')] = cssDef_([ + 'left: 0.1em', +]); +MENU_STYLES[makeClass_('SLIDERVALUE')] = cssDef_([ + 'position: absolute', + 'right: 0.1em', + 'padding-top: 0.25em', + 'color: #333333', + 'font-size: 0.75em', +]); +MENU_STYLES[makeClass_('MENUACTIVE') + ' ' + makeClass_('SLIDERVALUE')] = cssDef_([ + 'color: #DDDDDD', +]); +MENU_STYLES[makeClass_('SLIDERBAR')] = cssDef_([ + 'outline: none', + 'background: #D3D3D3', +]); +MENU_STYLES[makeClass_('MENULABEL')] = cssDef_([ + 'padding: 1px 2em 3px 1.33em', + 'font-style: italic', +]); +MENU_STYLES[makeClass_('MENURULE')] = cssDef_([ + 'border-top: 1px solid #DDDDDD', + 'margin: 4px 3px', +]); +MENU_STYLES[makeClass_('MENUDISABLED')] = cssDef_([ + 'color: GrayText', +]); +MENU_STYLES[makeClass_('MENUACTIVE')] = cssDef_([ + 'background-color: #606872', + 'color: white', +]); +MENU_STYLES[makeClass_('MENUDISABLED') + ':focus'] = cssDef_([ + 'background-color: #E8E8E8', +]); +MENU_STYLES[makeClass_('MENULABEL') + ':focus'] = cssDef_([ + 'background-color: #E8E8E8', +]); +MENU_STYLES[makeClass_('CONTEXTMENU') + ':focus'] = cssDef_([ + 'outline: none', +]); MENU_STYLES[ makeClass_('CONTEXTMENU') + ' ' + makeClass_('MENUITEM') + ':focus' -] = '{' + ' outline:none' + '}'; -MENU_STYLES[makeClass_('SELECTIONMENU')] = - '{' + - ' position:relative; float:left;' + - ' border-bottom: none;' + - ' -webkit-box-shadow:none;' + - ' -webkit-border-radius:0px; ' + - // ' border-bottom-style: none; ' + - '}'; -MENU_STYLES[makeClass_('SELECTIONITEM')] = '{' + ' padding-right: 1em;' + '}'; -MENU_STYLES[makeClass_('SELECTION')] = '{' + ' right: 40%; width:50%; ' + '}'; -MENU_STYLES[makeClass_('SELECTIONBOX')] = - '{' + - ' padding: 0em; max-height:20em; max-width: none;' + - ' background-color:#FFFFFF;' + - '}'; -MENU_STYLES[makeClass_('SELECTIONDIVIDER')] = - '{' + ' clear: both; border-top: 2px solid #000000;' + '}'; -MENU_STYLES[makeClass_('MENU') + ' ' + makeClass_('MENUCLOSE')] = - '{' + ' top:-10px; left:-10px' + '}'; +] = cssDef_([ + 'outline: none', +]); +MENU_STYLES[makeClass_('SELECTIONMENU')] = cssDef_([ + 'position: relative', + 'float: left', + 'border-bottom: none', + 'box-shadow: none ! important', + 'border-radius: 0px !important', +]); +MENU_STYLES[makeClass_('SELECTIONITEM')] = cssDef_([ + 'padding-right: 1em', +]); +MENU_STYLES[makeClass_('SELECTION')] = cssDef_([ + 'right: 40%', + 'width: 50%', +]); +MENU_STYLES[makeClass_('SELECTIONBOX')] = cssDef_([ + 'padding: 0em', + 'max-height: 20em', + 'max-width: none', + 'background-color: #FFFFFF', +]); +MENU_STYLES[makeClass_('SELECTIONDIVIDER')] = cssDef_([ + 'clear: both', + 'border-top: 2px solid #000000', +]); +MENU_STYLES[makeClass_('MENU') + ' ' + makeClass_('MENUCLOSE')] = cssDef_([ + 'top: -10px', + 'left: -10px', +]); +MENU_STYLES['@media (prefers-color-scheme: dark) /* menu */'] = cssDef_([ + makeClass_('MENU') + ' ' + cssDef_([ + 'color: #E0E0E0', + 'background-color: #242436', + 'box-shadow: 0px 10px 20px #000', + 'border: 1px solid #808080', + ]), + makeClass_('SLIDERVALUE') + ' ' + cssDef_([ + 'color: #D0D0D0', + ]), + makeClass_('MENUDISABLED') + ':focus ' + cssDef_([ + 'background-color: #383838', + ]), + makeClass_('MENULABEL') + ':focus ' + cssDef_([ + 'background-color: #585858', + ]), + makeClass_('MENURULE') + ' ' + cssDef_([ + 'border-top: 1px solid #808080', + ]), + makeClass_('SELECTIONDIVIDER') + ' ' + cssDef_([ + 'border-top: 2px solid #808080', + ]), +]); + // Style of the little cross button to close a dialog or the mobile menu. const CLOSE_ICON_STYLES: { [id: string]: string } = {}; -CLOSE_ICON_STYLES[makeClass_('MENUCLOSE')] = - '{' + - ' position:absolute;' + - ' cursor:pointer;' + - ' display:inline-block;' + - ' border:2px solid #AAA;' + - ' border-radius:18px;' + - ' -webkit-border-radius: 18px; /* Safari and Chrome */' + - ' -moz-border-radius: 18px; /* Firefox */' + - ' -khtml-border-radius: 18px; /* Konqueror */' + - ' font-family: "Courier New", Courier;' + - ' font-size:24px;' + - ' color:#F0F0F0' + - '}'; -CLOSE_ICON_STYLES[makeClass_('MENUCLOSE') + ' span'] = - '{' + - ' display:block; background-color:#AAA; border:1.5px solid;' + - ' border-radius:18px;' + - ' -webkit-border-radius: 18px; /* Safari and Chrome */' + - ' -moz-border-radius: 18px; /* Firefox */' + - ' -khtml-border-radius: 18px; /* Konqueror */' + - ' line-height:0;' + - ' padding:8px 0 6px /* may need to be browser-specific */' + - '}'; -CLOSE_ICON_STYLES[makeClass_('MENUCLOSE') + ':hover'] = - '{' + ' color:white!important;' + ' border:2px solid #CCC!important' + '}'; -CLOSE_ICON_STYLES[makeClass_('MENUCLOSE') + ':hover span'] = - '{' + ' background-color:#CCC!important' + '}'; -CLOSE_ICON_STYLES[makeClass_('MENUCLOSE') + ':hover:focus'] = - '{' + ' outline:none' + '}'; +CLOSE_ICON_STYLES[makeClass_('MENUCLOSE')] = cssDef_([ + 'position: absolute', + 'cursor: pointer', + 'display: inline-block', + 'border: 2px solid #AAA', + 'border-radius: 18px', + 'font-family: "Courier New", Courier', + 'font-size: 24px', + 'color: #F0F0F0', +]); +CLOSE_ICON_STYLES[makeClass_('MENUCLOSE') + ' span'] = cssDef_([ + 'display: block', + 'background-color: #AAA', + 'border: 1.5px solid', + 'border-radius: 18px', + 'line-height: 0', + 'padding: 8px 0 6px', +]); +CLOSE_ICON_STYLES[makeClass_('MENUCLOSE') + ':hover'] = cssDef_([ + 'color: white !important', + 'border: 2px solid #CCC !important', +]); +CLOSE_ICON_STYLES[makeClass_('MENUCLOSE') + ':hover span'] = cssDef_([ + 'background-color: #CCC !important', +]); +CLOSE_ICON_STYLES[makeClass_('MENUCLOSE') + ':hover:focus'] = cssDef_([ + 'outline: none', +]); -let INFO_ADDED = false; -let MENU_ADDED = false; -let CLOSE_ICON_ADDED = false; +const STYLESHEET_NAME = 'MJX-Menu-styles'; /** * Adds the CSS styles for context menus. - * @param opt_document The HTML document. + * @param document The HTML document. */ -export function addMenuStyles(opt_document: HTMLDocument): void { - if (MENU_ADDED) { - return; - } - addStyles_(MENU_STYLES, opt_document); - MENU_ADDED = true; - addCloseIconStyles_(opt_document); -} - -/** - * Adds the CSS styles for info widgets. - * @param opt_document The HTML document. - */ -export function addInfoStyles(opt_document: HTMLDocument): void { - if (INFO_ADDED) { - return; - } - addStyles_(INFO_STYLES, opt_document); - INFO_ADDED = true; - addCloseIconStyles_(opt_document); -} - -/** - * Adds the CSS style sheets for the close button. - * @param opt_document The HTML document. - */ -function addCloseIconStyles_(opt_document: HTMLDocument): void { - if (CLOSE_ICON_ADDED) { - return; - } - addStyles_(CLOSE_ICON_STYLES, opt_document); - CLOSE_ICON_ADDED = true; +export function addMenuStyles(document: Document): void { + addStyles_({...MENU_STYLES, /*...INFO_STYLES,*/ ...CLOSE_ICON_STYLES}, document); } /** * Creates style elements and adds them to the document. * @param styles The style dictionary. - * @param opt_document The HTML document. + * @param doc The HTML document. + * @param name The id to use for the stylesheet. */ function addStyles_( styles: { [id: string]: string }, - opt_document: HTMLDocument + doc: Document = document, + name: string = STYLESHEET_NAME, ): void { - const doc = opt_document || document; + if (doc.head.querySelector('#' + name)) { + return; + } const element: HTMLStyleElement = doc.createElement('style'); - element.type = 'text/css'; - let inner = ''; + element.id = name; + const inner = [] as string[]; for (const style in styles) { - inner += style; - inner += ' '; - inner += styles[style]; - inner += '\n'; + inner.push(`${style} ${styles[style]}`); } - element.innerHTML = inner; + element.textContent = inner.join('\n'); doc.head.appendChild(element); } From a76a7a426fb58ccec90d93cf942dcd7d66c5112a Mon Sep 17 00:00:00 2001 From: "Davide P. Cervone" Date: Mon, 20 Oct 2025 09:15:12 -0400 Subject: [PATCH 2/2] Fix problems affecting Safari (GrayText is too dark, and semicolons cause incorrect selectors in @media block) --- ts/css_util.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ts/css_util.ts b/ts/css_util.ts index 0738e33..83ab43d 100644 --- a/ts/css_util.ts +++ b/ts/css_util.ts @@ -35,8 +35,8 @@ function makeClass_(name: string): string { * @param def An array of css properties and their values. * @returns The css definition within braces. */ -function cssDef_(def: string[]) { - return '{\n ' + def.join(';\n ') + ';\n}'; +function cssDef_(def: string[], sep: string = ';') { + return '{\n ' + def.join(sep + '\n ') + ';\n}'; } /* @@ -186,7 +186,7 @@ MENU_STYLES[makeClass_('MENURULE')] = cssDef_([ 'margin: 4px 3px', ]); MENU_STYLES[makeClass_('MENUDISABLED')] = cssDef_([ - 'color: GrayText', + 'color: #999', ]); MENU_STYLES[makeClass_('MENUACTIVE')] = cssDef_([ 'background-color: #606872', @@ -256,7 +256,7 @@ MENU_STYLES['@media (prefers-color-scheme: dark) /* menu */'] = cssDef_([ makeClass_('SELECTIONDIVIDER') + ' ' + cssDef_([ 'border-top: 2px solid #808080', ]), -]); +], ''); // Style of the little cross button to close a dialog or the mobile menu.