Luke Van Epen
Tera Guru

Preface

This article describes methods which are rather hacky in nature. None of this is officially supported, is likely to break on future versions/evolutions of the UI, and only works under limited circumstance. Proceed at your own risk.

 

How to apply custom themes to ServiceNow script fields

find_real_file.png

 

Step 1: Install a userscript browser extension

You will need to install a user script extension which allows custom javascript/css injection. The one I used is available for chrome and opera, your mileage may vary on other extensions. User JavaScript and CSS

 

Step 2: Find the theme you want

Download a theme css file from this list here: https://codemirror.net/5/theme/

These themes are officially curated by CodeMirror (the editor SN uses) so they should produce a good result

Step 3: Edit the CSS file

You need to do the following things in order to get the CSS file to overwrite ServiceNow's default styling

1. Change all references of ".cm-s-stylename" to ".cm-s-snc" as this is the tag which ServiceNow has applied to in the HTML.

2. Add a "!important;" flag to the end of every CSS selector. Your file should look something like this at the end.


.cm-s-snc.CodeMirror, .cm-s-snc .CodeMirror-gutters {
  background-color: #282a36 !important;
  color: #f8f8f2 !important;
  border: none;
}
.cm-s-snc .CodeMirror-gutters { color: #282a36  !important}
.cm-s-snc .CodeMirror-cursor { border-left: solid thin #f8f8f0 !important }
.cm-s-snc .CodeMirror-linenumber { color: #6D8A88 !important }
.cm-s-snc .CodeMirror-selected { background: rgba(255, 255, 255, 0.10) !important}
.cm-s-snc .CodeMirror-line::selection, .cm-s-snc .CodeMirror-line > span::selection, .cm-s-snc .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10) !important}
.cm-s-snc .CodeMirror-line::-moz-selection, .cm-s-snc .CodeMirror-line > span::-moz-selection, .cm-s-snc .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10)  !important}
.cm-s-snc span.cm-comment { color: #6272a4;  !important}
.cm-s-snc span.cm-string, .cm-s-snc span.cm-string-2 { color: #f1fa8c  !important}
.cm-s-snc span.cm-number { color: #bd93f9 !important }
.cm-s-snc span.cm-variable { color: #50fa7b !important }
.cm-s-snc span.cm-variable-2 { color: white  !important}
.cm-s-snc span.cm-def { color: #50fa7b  !important}
.cm-s-snc span.cm-operator { color: #ff79c6  !important}
.cm-s-snc span.cm-keyword { color: #ff79c6 !important }
.cm-s-snc span.cm-atom { color: #bd93f9 !important }
.cm-s-snc span.cm-meta { color: #f8f8f2  !important}
.cm-s-snc span.cm-tag { color: #ff79c6  !important}
.cm-s-snc span.cm-attribute { color: #50fa7b  !important}
.cm-s-snc span.cm-qualifier { color: #50fa7b !important }
.cm-s-snc span.cm-property { color: #66d9ef  !important}
.cm-s-snc span.cm-builtin { color: #50fa7b  !important}
.cm-s-snc span.cm-variable-3, .cm-s-snc span.cm-type { color: #ffb86c  !important}

.cm-s-snc .CodeMirror-activeline-background { background: rgba(255,255,255,0.1)  !important}
.cm-s-snc .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; }

 

Step 4: Add this code to the Javascript section:

const userStyles = `
.CodeMirror-code div { font-size:15px !important; font-family: "Fira Code" !important; }
.cm-s-snc.CodeMirror, .cm-s-snc .CodeMirror-gutters {
  background-color: #282a36 !important;
  color: #f8f8f2 !important;
  border: none;
}
.cm-s-snc .CodeMirror-gutters { color: #282a36  !important}
.cm-s-snc .CodeMirror-cursor { border-left: solid thin #f8f8f0 !important }
.cm-s-snc .CodeMirror-linenumber { color: #6D8A88 !important }
.cm-s-snc .CodeMirror-selected { background: rgba(255, 255, 255, 0.10) !important}
.cm-s-snc .CodeMirror-line::selection, .cm-s-snc .CodeMirror-line > span::selection, .cm-s-snc .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10) !important}
.cm-s-snc .CodeMirror-line::-moz-selection, .cm-s-snc .CodeMirror-line > span::-moz-selection, .cm-s-snc .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10)  !important}
.cm-s-snc span.cm-comment { color: #6272a4;  !important}
.cm-s-snc span.cm-string, .cm-s-snc span.cm-string-2 { color: #f1fa8c  !important}
.cm-s-snc span.cm-number { color: #bd93f9 !important }
.cm-s-snc span.cm-variable { color: #50fa7b !important }
.cm-s-snc span.cm-variable-2 { color: white  !important}
.cm-s-snc span.cm-def { color: #50fa7b  !important}
.cm-s-snc span.cm-operator { color: #ff79c6  !important}
.cm-s-snc span.cm-keyword { color: #ff79c6 !important }
.cm-s-snc span.cm-atom { color: #bd93f9 !important }
.cm-s-snc span.cm-meta { color: #f8f8f2  !important}
.cm-s-snc span.cm-tag { color: #ff79c6  !important}
.cm-s-snc span.cm-attribute { color: #50fa7b  !important}
.cm-s-snc span.cm-qualifier { color: #50fa7b !important }
.cm-s-snc span.cm-property { color: #66d9ef  !important}
.cm-s-snc span.cm-builtin { color: #50fa7b  !important}
.cm-s-snc span.cm-variable-3, .cm-s-snc span.cm-type { color: #ffb86c  !important}

.cm-s-snc .CodeMirror-activeline-background { background: rgba(255,255,255,0.1)  !important}
.cm-s-snc .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; }`;

// Make UserStyles  a copy of your CSS side, the below class simply is used to inject into the content view iframe.

class CSSInject {
	styles = "";
	href = "";
	id = 0;
	iframe = undefined;
	form_id = "";
	contentDocument = undefined;
	intervals = [];
	constructor(id){
		this.id = id;
		this.href = window.location.href;
		this.styles = userStyles; 
		var css = document.createElement('style'); 
	    css.type = 'text/css'; 
	
	    if (css.styleSheet)  
	        css.styleSheet.cssText = this.styles; 
	    else  
	        css.appendChild(document.createTextNode(this.styles)); 
	    this.css = css;
	}

	log(msg){
		console.log("CSSInject", msg);
	}
	
	setStylesIframe(){
		
	    /* Append style to the tag name */ 
	    this.contentDocument.getElementsByTagName("head")[0].appendChild(this.css); 
	    this.log("Styles set")
	}
	
	clearIntervals(){
		for(var i of this.intervals){
			clearInterval(i);
		}
	}
	
	
	setIframe(element){
		if(element){
			this.iframe = element;
			this.log("New iframe set");
			var doc;
			if(element.document)
				doc = element.document;
			else if (element.contentDocument)
				doc = element.contentDocument;
			this.log(doc);
			if(typeof doc == "undefined"){
				throw new Error("contentDocument undefined");
			}
			if(doc.querySelector("title") == null){
				throw new Error("Page not loaded");
			}
			
			this.contentDocument = doc;
			this.log("contentDocument set");
			
			this.form_id = this.iframe.g_form.getUniqueValue();
			this.log("Form id set as " + this.form_id);
			
			return true;
		} else {
			throw new Error("Element not passed");
		}
	}
}


var inj = new CSSInject(Date.now());

if(window.location.href.includes("/now/nav/ui/classic")){
	loadStyles();
}

function loadStyles(){
	var int = setInterval(() => {
		inj.intervals.push(int);
		let success = false;
		try{
			
			inj.log("Trying to fetch iframe");
			success = inj.setIframe(window.gsft_main);
		} catch(e){
			console.error(e);
		}
		if(success){
			clearInterval(int);
			inj.log("Setting styles")
			inj.setStylesIframe();
			monitor();
		}
	}, 50)
}

function monitor() {
		window.gsft_main.addEventListener("unload", () => {
			inj.iframe = undefined;
			inj.contentDocument = undefined;
			inj.form_id = "";
			inj.log("Iframe unloaded, loading again");
			loadStyles();
		})
}


 

 

Tested and works in Polaris UI, both when popped in and popped out. Untested pre-polaris UI.

 

 

Comments
Martin Ivanov
Giga Sage
Giga Sage

This is useful, but the dependency of the navigation breaks it all...

I am using an extension called SN Developer productivity (available only for firefox), which is adding the style to the script fields everywhere.

Luke Van Epen
Tera Guru

You're right, I've figured out how to inject the CSS to the iframe, updated CSS and JS sections in the post above. 

Version history
Last update:
‎09-19-2022 07:33 PM
Updated by: