diff --git a/css/gui/tabs.css b/css/gui/tabs.css
index 2e52d21325ebb9c4ad8fd49562e1e56c010b9986..afa0a58b8123c4d52780f7985b9d774656867b74 100644
--- a/css/gui/tabs.css
+++ b/css/gui/tabs.css
@@ -52,8 +52,16 @@ div.tab-bar a.active {
 	cursor: default;
 }
 
-.tab-content:not(.noFade) {
+.tab-content {
 	display: none;
+}
+
+.tab-content.active {
+	display: unset;
+}
+
+.tab-content:not(.noFade) {
+	/*display: none;*/
 	padding: 6px 12px;
 	-webkit-animation: fadeEffect 0.3s;
 	animation: fadeEffect 0.3s;
diff --git a/src/002-config/mousetrapConfig.js b/src/002-config/mousetrapConfig.js
index dd39ff0c3d8b77d5ca4062e99907c29746b1c5b6..db63020ef25ca8bf526d27db67d23a91c7f93db6 100644
--- a/src/002-config/mousetrapConfig.js
+++ b/src/002-config/mousetrapConfig.js
@@ -328,12 +328,18 @@ App.UI.Hotkeys.add("next-child", {
 });
 App.UI.Hotkeys.add("Previous Tab", {
 	callback: function() {
+		// Basically telling two different systems to go to the left tab, but since we never have multiple tab bars on
+		// the same page (as that is bad UI design) the player won't notice.
 		App.UI.tabBar.openLeftTab();
+		App.UI.tabs.left();
 	}, combinations: []
 });
 App.UI.Hotkeys.add("Next Tab", {
 	callback: function() {
+		// Basically telling two different systems to go to the right tab, but since we never have multiple tab bars on
+		// the same page (as that is bad UI design) the player won't notice.
 		App.UI.tabBar.openRightTab();
+		App.UI.tabs.right();
 	}, combinations: []
 });
 App.UI.Hotkeys.add("walkpast", {
diff --git a/src/gui/tabs.js b/src/gui/tabs.js
new file mode 100644
index 0000000000000000000000000000000000000000..da794affab02357140d3f2de3e3219791a58ff5a
--- /dev/null
+++ b/src/gui/tabs.js
@@ -0,0 +1,135 @@
+App.UI.tabs = (function() {
+	/**
+	 * @type {Array<HTMLButtonElement>}
+	 */
+	let active = null;
+
+	class TabBar {
+		/**
+		 * @typedef tab
+		 * @property {string} name
+		 * @property {string} id
+		 * @property {Node} content
+		 * @property {string} [buttonClass]
+		 */
+
+		/**
+		 * @param {string} id Saved, should stay the same across versions. No spaces allowed
+		 */
+		constructor(id) {
+			this._id = id;
+			/**
+			 * @type {Array<tab>}
+			 */
+			this._tabs = [];
+		}
+
+		/**
+		 * @param {string} name Display name
+		 * @param {string} id Saved, should stay the same across versions
+		 * @param {Node} content
+		 * @param {string} [buttonClass] class applied to the button
+		 */
+		addTab(name, id, content, buttonClass) {
+			this._tabs.push({
+				name: name,
+				id: id,
+				content: content,
+				buttonClass: buttonClass,
+			});
+		}
+
+		/**
+		 * @returns {DocumentFragment}
+		 */
+		render() {
+			const f = new DocumentFragment();
+			const tabBar = document.createElement("div");
+			tabBar.classList.add("tab-bar");
+			f.append(tabBar);
+
+			/**
+			 * @type {Array<HTMLButtonElement>}
+			 */
+			const buttonList = [];
+			/**
+			 * @type {Array<HTMLDivElement>}
+			 */
+			const contentList = [];
+
+			let selected = true;
+			for (const tab of this._tabs) {
+				// check if selected. If this tab bar has no entry yet the first tab will be true and all other false.
+				if (V.tabChoice.hasOwnProperty(this._id)) {
+					selected = V.tabChoice[this._id] === tab.id;
+				}
+
+				const button = App.UI.DOM.appendNewElement("button", tabBar, tab.name, tab.buttonClass);
+				buttonList.push(button);
+				const contentHolder = App.UI.DOM.appendNewElement("div", f, tab.content, "tab-content");
+				contentList.push(contentHolder);
+
+				button.onclick = () => {
+					buttonList.forEach(c => { c.classList.remove("active"); });
+					contentList.forEach(c => { c.classList.remove("active"); });
+					button.classList.add("active");
+					contentHolder.classList.add("active");
+				};
+
+				if (selected) {
+					contentHolder.classList.add("active");
+					button.classList.add("active");
+				}
+
+				selected = false;
+			}
+
+			active = buttonList;
+
+			return f;
+		}
+	}
+
+	/**
+	 * Call on passageinit to clear the button/content cache.
+	 * Shouldn't matter, but we don't need to have huge DOM trees floating around.
+	 */
+	function clear() {
+		active = null;
+	}
+
+	function openLeft() {
+		if (active) {
+			const index = currentIndex();
+			if (index - 1 >= 0) {
+				active[index - 1].click();
+			}
+		}
+	}
+
+	function openRight() {
+		if (active) {
+			const index = currentIndex();
+			if (index > -1 && index + 1 < active.length) {
+				active[index + 1].click();
+			}
+		}
+	}
+
+	function currentIndex() {
+		for (let i = 0; i < active.length; i++) {
+			if (active[i].classList.contains("active")) {
+				return i;
+			}
+		}
+		return -1;
+	}
+
+	return {
+		TabBar: TabBar,
+		clear: clear,
+		left: openLeft,
+		right: openRight
+	};
+})();
+
diff --git a/src/npc/startingGirls/startingGirlsPassage.js b/src/npc/startingGirls/startingGirlsPassage.js
index 7f6b689fed0adbdb35c0e752a1305b1edfee46a8..528e098f34e5ad1e20b6d89a35a36c174e04cf1e 100644
--- a/src/npc/startingGirls/startingGirlsPassage.js
+++ b/src/npc/startingGirls/startingGirlsPassage.js
@@ -397,60 +397,20 @@ App.StartingGirls.passage = function() {
 	App.UI.DOM.appendNewElement("h2", el, "You are customizing this slave:");
 	el.append(App.Desc.longSlave(V.activeSlave, {market: "generic"}));
 
-	// TODO: move me
-	/**
-	 *
-	 * @param {string} id
-	 * @param {Node} element
-	 * @returns {HTMLSpanElement}
-	 */
-	function makeSpanIded(id, element) {
-		const span = document.createElement("span");
-		span.id = id;
-		span.append(element);
-		return span;
-	}
-
-	const tabCaptions = {
-		"profile": 'Profile',
-		"physical": 'Physical',
-		"upper": 'Upper',
-		"lower": 'Lower',
-		"mental": 'Mental',
-		"skills": 'Skills',
-		"stats": 'Stats',
-		"family": 'Family',
-		"bodyMods": 'Body Mods',
-		"salon": 'Salon',
-		"finalize": 'Finalize',
-	};
-
-	const tabBar = App.UI.DOM.appendNewElement("div", el, '', "tab-bar");
-	tabBar.append(
-		App.UI.tabBar.tabButton('profile', tabCaptions.profile),
-		App.UI.tabBar.tabButton('physical', tabCaptions.physical),
-		App.UI.tabBar.tabButton('upper', tabCaptions.upper),
-		App.UI.tabBar.tabButton('lower', tabCaptions.lower),
-		App.UI.tabBar.tabButton('mental', tabCaptions.mental),
-		App.UI.tabBar.tabButton('skills', tabCaptions.skills),
-		App.UI.tabBar.tabButton('stats', tabCaptions.stats),
-		App.UI.tabBar.tabButton('family', tabCaptions.family),
-		App.UI.tabBar.tabButton('body-mods', tabCaptions.bodyMods),
-		App.UI.tabBar.tabButton('salon', tabCaptions.salon),
-		App.UI.tabBar.tabButton('finalize', tabCaptions.finalize),
-	);
-
-	el.append(App.UI.tabBar.makeTab('profile', makeSpanIded("content-profile", App.StartingGirls.profile(V.activeSlave))));
-	el.append(App.UI.tabBar.makeTab('physical', makeSpanIded("content-physical", App.StartingGirls.physical(V.activeSlave))));
-	el.append(App.UI.tabBar.makeTab('upper', makeSpanIded("content-upper", App.StartingGirls.upper(V.activeSlave))));
-	el.append(App.UI.tabBar.makeTab('lower', makeSpanIded("content-lower", App.StartingGirls.lower(V.activeSlave))));
-	el.append(App.UI.tabBar.makeTab('mental', makeSpanIded("content-mental", App.StartingGirls.mental(V.activeSlave))));
-	el.append(App.UI.tabBar.makeTab('skills', makeSpanIded("content-skills", App.StartingGirls.skills(V.activeSlave))));
-	el.append(App.UI.tabBar.makeTab('stats', makeSpanIded("content-stats", App.StartingGirls.stats(V.activeSlave))));
-	el.append(App.UI.tabBar.makeTab('family', makeSpanIded("content-family", App.Intro.editFamily(V.activeSlave))));
-	el.append(App.UI.tabBar.makeTab('body-mods', makeSpanIded("content-body-mods", App.UI.bodyModification(V.activeSlave, true))));
-	el.append(App.UI.tabBar.makeTab('salon', makeSpanIded("content-salon", App.UI.salon(V.activeSlave, true))));
-	el.append(App.UI.tabBar.makeTab('finalize', makeSpanIded("content-finalize", App.StartingGirls.finalize(V.activeSlave))));
+	const tabBar = new App.UI.tabs.TabBar("StartingGirls");
+	tabBar.addTab("Profile", "profile", App.StartingGirls.profile(V.activeSlave));
+	tabBar.addTab("Physical", "physical", App.StartingGirls.physical(V.activeSlave));
+	tabBar.addTab("Upper", "upper", App.StartingGirls.upper(V.activeSlave));
+	tabBar.addTab("Lower", "lower", App.StartingGirls.lower(V.activeSlave));
+	tabBar.addTab("Mental", "mental", App.StartingGirls.mental(V.activeSlave));
+	tabBar.addTab("Skills", "skills", App.StartingGirls.skills(V.activeSlave));
+	tabBar.addTab("Stats", "stats", App.StartingGirls.stats(V.activeSlave));
+	tabBar.addTab("Family", "family", App.Intro.editFamily(V.activeSlave));
+	tabBar.addTab("Body Mods", "body-mods", App.UI.bodyModification(V.activeSlave, true));
+	tabBar.addTab("Salon", "salon", App.UI.salon(V.activeSlave, true));
+	tabBar.addTab("Finalize", "finalize", App.StartingGirls.finalize(V.activeSlave),
+		startingSlaveCost(V.activeSlave) > V.cash ? "show-warning" : undefined);
+	el.append(tabBar.render());
 
 	return el;
 };
diff --git a/src/zz1-last/setupEventHandlers.js b/src/zz1-last/setupEventHandlers.js
index 8ad72f2310c94cbd84210b3e47fc61fb59283a27..9cda9e368e9b93dfee0fc1d1886d96f9e00e418b 100644
--- a/src/zz1-last/setupEventHandlers.js
+++ b/src/zz1-last/setupEventHandlers.js
@@ -13,6 +13,7 @@ $(document).on(":passageinit", () => {
 		V.passageSwitchHandler();
 		delete V.passageSwitchHandler;
 	}
+	App.UI.tabs.clear();
 	profileEvents.passageinit();
 });