Skip to content

Instantly share code, notes, and snippets.

@beporter
Last active April 15, 2024 15:41
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save beporter/704eea76b3943c50f7a61e287f60eca8 to your computer and use it in GitHub Desktop.
Save beporter/704eea76b3943c50f7a61e287f60eca8 to your computer and use it in GitHub Desktop.
A Greasemonkey/Tampermonkey script that allows you to customize the header color of each GitHub repository you frequent. If you already have Tampermonkey installed, click the "Raw" button to install this user script.
// ==UserScript==
// @name GitHub Repo Header Colors
// @namespace https://gist.github.com/beporter/704eea76b3943c50f7a61e287f60eca8
// @version 1.1.6
// @description Change the header background color of selected GitHub repositories.
// @author https://github.com/beporter
// @match http*://github.com/*
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_addStyle
// @run-at document-end
// @downloadURL https://gist.githubusercontent.com/beporter/704eea76b3943c50f7a61e287f60eca8/raw/github_header_colors.user.js
// ==/UserScript==
(function () {
'use strict';
var GithubHeaderColor = {
/**
* Hex value of GitHub's default header color. Used to determine if we have a
* customized color stored for the repo in the active page or not. Set during
* this.init().
*/
defaultBackgroundColor: '#fafbfc',
/**
* Stores the DOM element for the color picker <input> at runtime. Set via
* this.getPicker() during this.init().
*/
pickerEl: null,
/**
* Stores the DOM element representing the "header" bar on the page.
*/
headerEl: null,
/**
* Used to initialize a new instance of this class. Collects necessary
* information from the page and GreaseMonkey local storage, injects the color
* picker into the page for use and registers an event listener for pjax page
* changes.
*/
init: function () {
if (!this.getRepoName()) {
return; // Do nothing on non-repo Github pages.
}
this.defaultBackgroundColor = this.getHeaderColor();
GM_addStyle('.AppHeader-button, .AppHeader-searchButton { background-color: var(--color-canvas-inset) !important; }');
this.execute();
document.addEventListener('pjax:success', this.execute.bind(this));
document.addEventListener('turbo:render', this.execute.bind(this));
},
/**
* Attach the picker input element into the DOM and set the on-page header color.
*/
execute: function () {
this.attachPicker(this.getPicker(this.loadRepoColor()));
this.setHeaderColor(this.loadRepoColor());
},
/**
* Look up the name of the repo that the current page represents by fetching a
* <meta> tag value.
*
* @returns {string} The name of the current repository, empty string if none.
*/
getRepoName: function () {
var options = [
'meta[name="octolytics-dimension-repository_nwo"]',
'meta[property="og:title"]'
];
const fetcher = function (selector) {
const tag = document.querySelector(selector);
if (!tag) { return '' };
return tag.getAttribute('content');
};
const finder = function (name) {
return !!fetcher(name).match(/^[\w_-]+\/[\w_-]+$/);
};
return fetcher(options.find(finder)) || '';
},
/**
* Fetches the header element from the DOM by querySelector lookup.
* Can't be cached since the element gets replaced by pjax on each "page" load.
*/
getHeader: function () {
return document.querySelector('.AppHeader-globalBar');
//return document.querySelector('#repository-container-header');
//return document.querySelector('.pagehead-actions').parentElement.parentElement;
},
/**
* Fetches the current CSS color for the page header as a 7-char hex value.
*
* @returns {string} 7-char hex color value of the current page header.
*/
getHeaderColor: function () {
var rgb = this.getHeader().style.backgroundColor;
if (rgb && rgb.length) {
return this.toHex(rgb);
}
return this.defaultBackgroundColor;
},
/**
* Set CSS color for the page header.
*
* @param {string} color 7-char hex color value to set for the current page
* header.
* @param {decimal} transitionTime Time in seconds to transition from existing
* color to new color. Default = 0.
*/
setHeaderColor: function (color, transitionTime = 0) {
this.getHeader().style.transition = 'background-color ' + transitionTime + 's';
this.getHeader().style.setProperty('background-color', color, 'important');
//document.querySelector('.js-repo-nav').style.setProperty('background-color', 'transparent', 'important');
},
/**
* Loads the currently used page color from Greasmonkey's browser-local
* storage. Returns the default color if none is explicitly set.
*
* @returns {string} A 7-character hex color string representing the "proper"
* color for the provided repo.
*/
loadRepoColor: function () {
return GM_getValue(this.keyName(), this.defaultBackgroundColor);
},
/**
* Saves the provided color to in Greasmonkey's browser-local storage for the
* current repo name. The save is skipped if the color matches the default.
*
* @param {string} color A 7-character hex color string representing the color
* to save to local storage for the current repo.
*/
saveRepoColor: function (color) {
if (color !== this.defaultBackgroundColor) {
GM_setValue(this.keyName(), color);
}
},
/**
* Attaches the provided element to the page DOM, wrapping it in an <li>
* container.
*
* @param {object} el A DOM element representing the color picker to use. Should
* already have an onchange event listener attached.
*/
attachPicker: function (el) {
var container = document.createElement('li');
container.appendChild(el);
var par = document.querySelector('.AppHeader-context-full ul');
if (par.querySelector('#GithubRepoHeaderColor_Picker') == null) {
par.appendChild(container);
}
},
/**
* Returns the existing this.pickerEl when available, otherwise builds a new one
* from scratch.
*
* @param {string} defaultColor A 7-character hex color string to be used as the
* "existing" color of a freshly generated picker.
* @returns {object} The new or existing `<input type=color>` object.
*/
getPicker: function (defaultColor) {
if (!this.pickerEl) {
this.pickerEl = this.createPicker(defaultColor);
}
return this.pickerEl;
},
/**
* Builds a new `<input type=color>` from scratch.
*
* @param {string} defaultColor A 7-character hex color string to be
* used as the "existing" color of a freshly generated picker.
* @returns {object} A new `<input type=color>` object.
*/
createPicker: function (defaultColor) {
var picker = document.createElement('input');
picker.type = 'color';
picker.value = defaultColor;
picker.title = 'Change the header color for this repository.';
picker.className = 'AppHeader-button';
picker.id = 'GithubRepoHeaderColor_Picker';
//picker.style = 'height:28px;';
picker.onchange = this.onPickerChange.bind(this);
return picker;
},
/**
* Event listener that handles the onChange event for the color picker.
*
* Looks for a new selected color. When found, sets the header and saves the
* value to Greasemonkey browser-local storage for the current repo.
*
* @param {object} event The onChange event being fired.
*/
onPickerChange: function (event) {
var newColor = this.getPicker(this.loadRepoColor()).value;
if (newColor !== this.getHeaderColor()) {
this.setHeaderColor(newColor, 1);
this.saveRepoColor(newColor);
}
},
/**
* Internal helper that builds a key name for the current repo. Used
* when fetching or storing the color for the given repo in Greasemonkey's
* browser-local storage.
*
* @returns {string} The key to use when looking up or saving the color
* value for the current page.
*/
keyName: function () {
return 'GH_Repo_Colors_' + this.getRepoName().replace('/', '_');
},
/**
* Internal helper to convert a string like `rgb(25,100,255)` to
* `#1964FF`.
*
* @param {string} rgbStr A string like `rgb(25,100,255)`.
* @returns {string} The hex formatted equivalent value, like `#1964FF`.
*/
toHex: function (rgbStr) {
return '#' + rgbStr
.substr(4, rgbStr.indexOf(')') - 4)
.split(',')
.map((color) => parseInt(color).toString(16))
.join('');
},
};
GithubHeaderColor.init();
}());
@beporter
Copy link
Author

Demo gif:
github-repo-header-color

@beporter
Copy link
Author

beporter commented Oct 14, 2019

Updated the script so it can work with Github Enterprise (self-hosted) instances too. If you are using Tampermonkey, open the script and switch to the Settings tab. Under User matches:, add an entry for http*://YOUR_GIHUB_ENTERPRISE_DOMAIN/* to enable this script.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment