ソースを参照

Add light theme toggle

Markus-Rost 4 年 前
コミット
8a6b94bb86

+ 7 - 3
dashboard/guilds.js

@@ -23,12 +23,13 @@ const file = require('fs').readFileSync('./dashboard/index.html');
  * Let a user view settings
  * @param {import('http').ServerResponse} res - The server response
  * @param {import('./i18n.js')} dashboardLang - The user language.
+ * @param {String} theme - The display theme
  * @param {String} state - The user state
  * @param {URL} reqURL - The used url
  * @param {String} [action] - The action the user made
  * @param {String[]} [actionArgs] - The arguments for the action
  */
-function dashboard_guilds(res, dashboardLang, state, reqURL, action, actionArgs) {
+function dashboard_guilds(res, dashboardLang, theme, state, reqURL, action, actionArgs) {
 	reqURL.pathname = reqURL.pathname.replace( /^(\/(?:guild\/\d+(?:\/(?:settings|verification|rcscript)(?:\/(?:\d+|new))?)?)?)(?:\/.*)?$/, '$1' );
 	var args = reqURL.pathname.split('/');
 	args = reqURL.pathname.split('/');
@@ -40,6 +41,7 @@ function dashboard_guilds(res, dashboardLang, state, reqURL, action, actionArgs)
 	res.setHeader('Content-Language', [dashboardLang.lang]);
 	var $ = cheerio.load(file);
 	$('html').attr('lang', dashboardLang.lang);
+	if ( theme === 'light' ) $('html').addClass('theme-light');
 	$('<script>').text(`
 		const selectLanguage = '${dashboardLang.get('general.language').replace( /'/g, '\\$&' )}';
 		const allLangs = ${JSON.stringify(allLangs)};
@@ -48,11 +50,13 @@ function dashboard_guilds(res, dashboardLang, state, reqURL, action, actionArgs)
 	$('.channel#settings div').text(dashboardLang.get('general.settings'));
 	$('.channel#verification div').text(dashboardLang.get('general.verification'));
 	$('.channel#rcscript div').text(dashboardLang.get('general.rcscript'));
+	$('.guild#invite a').attr('alt', dashboardLang.get('general.invite'));
+	$('.guild#refresh a').attr('alt', dashboardLang.get('general.refresh'));
+	$('.guild#theme-dark a').attr('alt', dashboardLang.get('general.theme-dark'));
+	$('.guild#theme-light a').attr('alt', dashboardLang.get('general.theme-light'));
 	$('#selector span').text(dashboardLang.get('general.selector'));
 	$('#support span').text(dashboardLang.get('general.support'));
 	$('#logout').attr('alt', dashboardLang.get('general.logout'));
-	$('.guild#invite a').attr('alt', dashboardLang.get('general.invite'));
-	$('.guild#refresh a').attr('alt', dashboardLang.get('general.refresh'));
 	if ( process.env.READONLY ) createNotice($, 'readonly', dashboardLang);
 	if ( action ) createNotice($, action, dashboardLang, actionArgs);
 	$('head').append(

+ 2 - 0
dashboard/i18n/en.json

@@ -18,6 +18,8 @@
         "selector": "Server Selector",
         "settings": "Settings",
         "support": "Support Server",
+        "theme-dark": "Use dark theme",
+        "theme-light": "Use light theme",
         "title": "Wiki-Bot Settings",
         "verification": "Verifications",
         "welcome": "<h2>Welcome to the Wiki-Bot Dashboard.</h2>\n<p>Wiki-Bot is a Discord bot made to bring Discord servers and MediaWiki wikis together. It helps with linking wiki pages, verifying wiki users, informing about latest changes on the wiki and more. <a href=\"https://wiki.wikibot.de/wiki/Wiki-Bot_Wiki\" target=\"_blank\">[More information]</a></p>\n<p>Here you can change different bot settings for servers you have Manage Server permission on. To begin, you will have to authenticate your Discord account which you can do with this button:</p>"

+ 31 - 0
dashboard/index.html

@@ -72,6 +72,37 @@
 					</div>
 				</a>
 			</div>
+			<div class="guild" id="theme-separator" style="display: none;">
+				<div class="separator"></div>
+			</div>
+			<div class="guild" id="theme-light" style="display: none;">
+				<div class="bar"></div>
+				<a alt="Use light theme">
+					<div class="avatar noicon">
+						<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
+							<circle cx="12" cy="12" r="5"/>
+							<line x1="12" y1="1" x2="12" y2="3"/>
+							<line x1="12" y1="21" x2="12" y2="23"/>
+							<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/>
+							<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/>
+							<line x1="1" y1="12" x2="3" y2="12"/>
+							<line x1="21" y1="12" x2="23" y2="12"/>
+							<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/>
+							<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/>
+						</svg>
+					</div>
+				</a>
+			</div>
+			<div class="guild" id="theme-dark" style="display: none;">
+				<div class="bar"></div>
+				<a alt="Use dark theme">
+					<div class="avatar noicon">
+						<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
+							<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
+						</svg>
+					</div>
+				</a>
+			</div>
 		</div>
 	</div>
 </body>

+ 13 - 6
dashboard/index.js

@@ -92,6 +92,9 @@ const server = http.createServer( (req, res) => {
 			 * @param {String[]} [actionArgs]
 			 */
 			function save_response(resURL = '/', action, ...actionArgs) {
+				var themeCookie = ( req.headers?.cookie?.split('; ')?.find( cookie => {
+					return cookie.split('=')[0] === 'theme' && /^"(?:light|dark)"$/.test(( cookie.split('=')[1] || '' ));
+				} ) || 'dark' ).replace( /^theme="(light|dark)"$/, '$1' );
 				var langCookie = ( req.headers?.cookie?.split('; ')?.filter( cookie => {
 					return cookie.split('=')[0] === 'language' && /^"[a-z\-]+"$/.test(( cookie.split('=')[1] || '' ));
 				} )?.map( cookie => cookie.replace( /^language="([a-z\-]+)"$/, '$1' ) ) || [] );
@@ -105,7 +108,7 @@ const server = http.createServer( (req, res) => {
 					return '';
 				} ) || [] ));
 				dashboardLang.fromCookie = langCookie;
-				return dashboard(res, dashboardLang, state, new URL(resURL, process.env.dashboard), action, actionArgs);
+				return dashboard(res, dashboardLang, themeCookie, state, new URL(resURL, process.env.dashboard), action, actionArgs);
 			}
 		}
 	}
@@ -131,6 +134,10 @@ const server = http.createServer( (req, res) => {
 
 	res.setHeader('Content-Type', 'text/html');
 
+	var themeCookie = ( req.headers?.cookie?.split('; ')?.find( cookie => {
+		return cookie.split('=')[0] === 'theme' && /^"(?:light|dark)"$/.test(( cookie.split('=')[1] || '' ));
+	} ) || 'dark' ).replace( /^theme="(light|dark)"$/, '$1' );
+
 	var langCookie = ( req.headers?.cookie?.split('; ')?.filter( cookie => {
 		return cookie.split('=')[0] === 'language' && /^"[a-z\-]+"$/.test(( cookie.split('=')[1] || '' ));
 	} )?.map( cookie => cookie.replace( /^language="([a-z\-]+)"$/, '$1' ) ) || [] );
@@ -158,7 +165,7 @@ const server = http.createServer( (req, res) => {
 	if ( reqURL.pathname === '/login' ) {
 		let action = '';
 		if ( reqURL.searchParams.get('action') === 'failed' ) action = 'loginfail';
-		return pages.login(res, dashboardLang, state, action);
+		return pages.login(res, dashboardLang, themeCookie, state, action);
 	}
 
 	if ( reqURL.pathname === '/logout' ) {
@@ -167,7 +174,7 @@ const server = http.createServer( (req, res) => {
 			...( res.getHeader('Set-Cookie') || [] ),
 			'wikibot=""; HttpOnly; Path=/; Max-Age=0'
 		]);
-		return pages.login(res, dashboardLang, state, 'logout');
+		return pages.login(res, dashboardLang, themeCookie, state, 'logout');
 	}
 
 	if ( !state ) {
@@ -177,7 +184,7 @@ const server = http.createServer( (req, res) => {
 				res.setHeader('Set-Cookie', [`guild="${pathGuild}"; HttpOnly; Path=/`]);
 			}
 		}
-		return pages.login(res, dashboardLang, state, ( reqURL.pathname === '/' ? '' : 'unauthorized' ));
+		return pages.login(res, dashboardLang, themeCookie, state, ( reqURL.pathname === '/' ? '' : 'unauthorized' ));
 	}
 
 	if ( reqURL.pathname === '/oauth' ) {
@@ -191,7 +198,7 @@ const server = http.createServer( (req, res) => {
 				res.setHeader('Set-Cookie', [`guild="${pathGuild}"; HttpOnly; Path=/`]);
 			}
 		}
-		return pages.login(res, dashboardLang, state, ( reqURL.pathname === '/' ? '' : 'unauthorized' ));
+		return pages.login(res, dashboardLang, themeCookie, state, ( reqURL.pathname === '/' ? '' : 'unauthorized' ));
 	}
 
 	if ( reqURL.pathname === '/refresh' ) {
@@ -210,7 +217,7 @@ const server = http.createServer( (req, res) => {
 	let action = '';
 	if ( reqURL.searchParams.get('refresh') === 'success' ) action = 'refresh';
 	if ( reqURL.searchParams.get('refresh') === 'failed' ) action = 'refreshfail';
-	return dashboard(res, dashboardLang, state, reqURL, action);
+	return dashboard(res, dashboardLang, themeCookie, state, reqURL, action);
 } );
 
 server.listen( 8080, 'localhost', () => {

+ 31 - 0
dashboard/login.html

@@ -65,6 +65,37 @@
 					</div>
 				</a>
 			</div>
+			<div class="guild" id="theme-separator" style="display: none;">
+				<div class="separator"></div>
+			</div>
+			<div class="guild" id="theme-light" style="display: none;">
+				<div class="bar"></div>
+				<a alt="Use light theme">
+					<div class="avatar noicon">
+						<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
+							<circle cx="12" cy="12" r="5"/>
+							<line x1="12" y1="1" x2="12" y2="3"/>
+							<line x1="12" y1="21" x2="12" y2="23"/>
+							<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/>
+							<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/>
+							<line x1="1" y1="12" x2="3" y2="12"/>
+							<line x1="21" y1="12" x2="23" y2="12"/>
+							<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/>
+							<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/>
+						</svg>
+					</div>
+				</a>
+			</div>
+			<div class="guild" id="theme-dark" style="display: none;">
+				<div class="bar"></div>
+				<a alt="Use dark theme">
+					<div class="avatar noicon">
+						<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
+							<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
+						</svg>
+					</div>
+				</a>
+			</div>
 		</div>
 	</div>
 </body>

+ 5 - 1
dashboard/oauth.js

@@ -18,10 +18,11 @@ const file = require('fs').readFileSync('./dashboard/login.html');
  * Let a user login
  * @param {import('http').ServerResponse} res - The server response
  * @param {import('./i18n.js')} dashboardLang - The user language.
+ * @param {String} theme - The display theme
  * @param {String} [state] - The user state
  * @param {String} [action] - The action the user made
  */
-function dashboard_login(res, dashboardLang, state, action) {
+function dashboard_login(res, dashboardLang, theme, state, action) {
 	if ( state && settingsData.has(state) ) {
 		if ( !action ) {
 			res.writeHead(302, {Location: '/'});
@@ -31,6 +32,7 @@ function dashboard_login(res, dashboardLang, state, action) {
 	}
 	var $ = cheerio.load(file);
 	$('html').attr('lang', dashboardLang.lang);
+	if ( theme === 'light' ) $('html').addClass('theme-light');
 	$('<script>').text(`
 		const selectLanguage = '${dashboardLang.get('general.language').replace( /'/g, '\\$&' )}';
 		const allLangs = ${JSON.stringify(allLangs)};
@@ -39,6 +41,8 @@ function dashboard_login(res, dashboardLang, state, action) {
 	$('#login-button span, .channel#login div').text(dashboardLang.get('general.login'));
 	$('.channel#invite-wikibot div').text(dashboardLang.get('general.invite'));
 	$('.guild#invite a').attr('alt', dashboardLang.get('general.invite'));
+	$('.guild#theme-dark a').attr('alt', dashboardLang.get('general.theme-dark'));
+	$('.guild#theme-light a').attr('alt', dashboardLang.get('general.theme-light'));
 	$('#support span').text(dashboardLang.get('general.support'));
 	$('#text .description #welcome').html(dashboardLang.get('general.welcome'));
 	let responseCode = 200;

+ 211 - 105
dashboard/src/index.css

@@ -10,6 +10,10 @@ body {
 	min-height: 100%;
 	margin: 0;
 }
+.theme-light body {
+	background-color: #ffffff;
+	color: #2e3338;
+}
 a {
 	text-decoration: none;
 	color: inherit;
@@ -17,96 +21,19 @@ a {
 a[alt]:hover:after {
 	content: attr(alt);
 	position: absolute;
-	background: #000000;
+	background: #18191c;
 	color: #dcddde;
 	font-weight: bold;
 	font-size: 90%;
 	word-break: break-word;
 	border-radius: 4px;
 	padding: 8px;
+	box-shadow: 0 8px 16px rgba(0,0,0,0.24);
 }
-.description a,
-a .description,
-.notice a {
-	color: #00b0f4;
-}
-.description a:hover,
-a:hover .description,
-.notice a:hover {
-	text-decoration: underline;
-}
-code {
-	background: #2f3136;
-	padding: 1px 3px;
-	margin: -2px 0;
-	border: 1px solid #202225;
-	border-radius: 3px;
-	white-space: pre-wrap;
-	font-size: 120%;
-}
-#text {
-	position: relative;
-	padding: 8px;
-	width: calc(100% - 328px);
-	top: 48px;
-	left: 312px;
-}
-.user-select {
-	-webkit-user-select: all;
-	-moz-user-select: all;
-	-ms-user-select: all;
-	user-select: all;
-}
-.notice {
-	padding: 5px 10px;
-	line-height: 1.6;
-	text-align: center;
-	margin: 0 auto 1em;
-	width: -moz-fit-content;
-	width: fit-content;
-	border: 2px solid;
-}
-.notice-success {
-	background-color: #020;
-	border-color: #050;
-}
-.notice-info {
-	background-color: #220;
-	border-color: #550;
-}
-.notice-error {
-	background-color: #200;
-	border-color: #500;
-}
-.server-selector {
-	display: flex;
-	flex-wrap: wrap;
-}
-.server {
-	background-color: rgba(0,0,0,0.5);
-	text-align: center;
-	border-radius: 10%;
-	width: 200px;
-	margin: 5px;
-}
-.server:hover {
-	background: rgba(0,0,0,0.3);
-	filter: brightness(1.2);
-}
-.server .avatar {
-	border-radius: 10%;
-	width: 200px;
-	height: 200px;
-}
-.server .noicon {
-	font-size: 416%;
-	background-color: unset;
-}
-.server .server-name {
-	padding: 8px 12px;
-	word-break: break-word;
-	font-weight: bold;
-	font-size: 90%;
+.theme-light a[alt]:hover:after {
+	background-color: #ffffff;
+	color: #2e3338;
+	box-shadow: 0 8px 16px rgba(0,0,0,0.16);
 }
 #navbar {
 	background: #2f3136;
@@ -125,6 +52,12 @@ code {
 				0 1.5px 0 rgba(6,6,7,0.05),
 				0 2px 0 rgba(4,4,5,0.05);
 }
+.theme-light #navbar {
+	background-color: #f2f3f5;
+	box-shadow: 0 1px 0 rgba(6,6,7,0.1),
+				0 1.5px 0 rgba(6,6,7,0.025),
+				0 2px 0 rgba(6,6,7,0.025);
+}
 :target::before {
 	content: "";
 	display: block;
@@ -141,6 +74,9 @@ code {
 #navbar a:hover {
 	background: #202225;
 }
+.theme-light #navbar a:hover {
+	background-color: #e3e5e8;
+}
 #navbar a[alt]:hover:after {
 	top: 48px;
 }
@@ -175,6 +111,9 @@ code {
 	min-height: calc(100% - 24px);
 	width: 72px;
 }
+.theme-light #guildlist {
+	background-color: #e3e5e8;
+}
 .guild {
 	margin: 0 0 8px;
 	position: relative;
@@ -194,6 +133,9 @@ code {
 	color: #dcddde;
 	font-weight: bold;
 }
+.theme-light .avatar {
+	color: #2e3338;
+}
 #navbar a:hover .avatar,
 .guild.selected .avatar,
 .guild:hover .avatar {
@@ -205,14 +147,22 @@ code {
 	height: 48px;
 	background: #36393f;
 }
+.theme-light .noicon {
+	background-color: #ffffff;
+}
 .guild.selected .noicon,
 .guild:hover .noicon {
+	color: #ffffff;
 	background-color: #7289da;
 }
 .svg-avatar {
 	color: #43b581;
 	background: #36393f;
 }
+.theme-light .svg-avatar {
+	color: #43b581;
+	background-color: #ffffff;
+}
 .guild:hover .svg-avatar {
 	color: #ffffff;
 	background-color: #43b581;
@@ -223,6 +173,9 @@ code {
 	border-radius: 1px;
 	background-color: rgba(255,255,255,0.06);
 }
+.theme-light .separator {
+	background-color: rgba(6,6,7,0.08);
+}
 .bar {
 	position: absolute;
 	left: 0;
@@ -233,6 +186,9 @@ code {
 	margin-left: -4px;
 	background-color: #ffffff;
 }
+.theme-light .bar {
+	background-color: #060607;
+}
 .guild:hover .bar {
 	margin-top: 14px;
 	height: 20px;
@@ -257,6 +213,9 @@ code {
 	left: 72px;
 	bottom: 0;
 }
+.theme-light #channellist {
+	background-color: #f2f3f5;
+}
 .channel {
 	padding: 0 8px;
 	margin: 0 8px 2px 12px;
@@ -266,6 +225,9 @@ code {
 	align-items: center;
 	color: #8e9297;
 }
+.theme-light .channel {
+	color: #6a7480;
+}
 .channel img {
 	margin-right: 6px;
 	width: 20px;
@@ -284,15 +246,27 @@ code {
 .channel:hover {
 	background: rgba(79,84,92,0.16);
 }
+.theme-light .channel:hover {
+	background-color: rgba(116,127,141,0.08);
+}
 .channel.selected {
 	background: rgba(79,84,92,0.32);
 }
+.theme-light .channel.selected {
+	background-color: rgba(116,127,141,0.24);
+}
 .channel:hover div {
 	color: #dcddde;
 }
+.theme-light .channel:hover div {
+	color: #2e3338;
+}
 .channel.selected div {
 	color: #ffffff;
 }
+.theme-light .channel.selected div {
+	color: #060607;
+}
 .channel-header {
 	margin-left: 8px;
 	height: 44px;
@@ -320,6 +294,11 @@ code {
 	align-items: center;
 	color: #8e9297;
 }
+.theme-light #lang-selector {
+	background-color: #f2f3f5;
+	border-top-color: #e3e5e8;
+	color: #6a7480;
+}
 #lang-selector img {
 	padding-right: 2px;
 }
@@ -335,6 +314,9 @@ code {
 	bottom: 32px;
 	overflow: overlay;
 }
+.theme-light #lang-dropdown {
+	background-color: #e3e5e8;
+}
 #lang-dropdown div {
 	cursor: pointer;
 	margin: 2px;
@@ -342,9 +324,154 @@ code {
 	font-size: 90%;
 	border-radius: 2px;
 }
-#lang-dropdown div:hover, #lang-dropdown div.current {
+#lang-dropdown div.current,
+#lang-dropdown div:hover {
 	background: #2f3136;
 }
+.theme-light #lang-dropdown div.current,
+.theme-light #lang-dropdown div:hover {
+	background-color: #f2f3f5;
+}
+.description a,
+a .description,
+.notice a {
+	color: #00b0f4;
+}
+.theme-light .description a,
+.theme-light a .description,
+.theme-light .notice a {
+	color: #0067e0;
+}
+.description a:hover,
+a:hover .description,
+.notice a:hover {
+	text-decoration: underline;
+}
+code {
+	background: #2f3136;
+	padding: 1px 3px;
+	margin: -2px 0;
+	border: 1px solid #202225;
+	border-radius: 3px;
+	white-space: pre-wrap;
+	font-size: 120%;
+}
+.theme-light code {
+	background-color: #f2f3f5;
+	border-color: #e3e5e8;
+}
+#text {
+	position: relative;
+	padding: 8px;
+	width: calc(100% - 328px);
+	top: 48px;
+	left: 312px;
+}
+.user-select {
+	-webkit-user-select: all;
+	-moz-user-select: all;
+	-ms-user-select: all;
+	user-select: all;
+}
+.notice {
+	padding: 5px 10px;
+	line-height: 1.6;
+	text-align: center;
+	margin: 0 auto 1em;
+	width: -moz-fit-content;
+	width: fit-content;
+	border: 2px solid;
+}
+.notice-success {
+	background-color: #020;
+	border-color: #050;
+}
+.theme-light .notice-success {
+	background-color: #cfc;
+	border-color: #afa;
+}
+.notice-info {
+	background-color: #220;
+	border-color: #550;
+}
+.theme-light .notice-info {
+	background-color: #ffc;
+	border-color: #ffa;
+}
+.notice-error {
+	background-color: #200;
+	border-color: #500;
+}
+.theme-light .notice-error {
+	background-color: #fcc;
+	border-color: #faa;
+}
+#login-button {
+	display: flex;
+	margin: 20px auto;
+	padding: 20px 50px;
+	width: -moz-fit-content;
+	width: fit-content;
+	justify-content: center;
+	align-items: center;
+	font-size: 300%;
+	background: #2f3136;
+	border: 5px solid #202225;
+	border-radius: 30px;
+}
+.theme-light #login-button {
+	background-color: #f2f3f5;
+	border-color: #e3e5e8;
+}
+#login-button:hover {
+	background-color: #36393f;
+}
+.theme-light #login-button:hover {
+	background-color: #fff;
+}
+#login-button img {
+	width: 60px;
+	height: 60px;
+	padding-right: 10px;
+}
+.server-selector {
+	display: flex;
+	flex-wrap: wrap;
+}
+.server {
+	background-color: #202225;
+	text-align: center;
+	border-radius: 10%;
+	width: 200px;
+	margin: 5px;
+}
+.theme-light .server {
+	background-color: #e3e5e8;
+}
+.server:hover {
+	background-color: #2f3136;
+}
+.theme-light .server:hover {
+	background-color: #f2f3f5;
+}
+.server:hover .avatar {
+	filter: brightness(1.2);
+}
+.server .avatar {
+	border-radius: 10%;
+	width: 200px;
+	height: 200px;
+}
+.server .noicon {
+	font-size: 416%;
+	background-color: unset;
+}
+.server .server-name {
+	padding: 8px 12px;
+	word-break: break-word;
+	font-weight: bold;
+	font-size: 90%;
+}
 fieldset > div {
 	margin: 10px 0;
 }
@@ -388,25 +515,4 @@ button.addmore:not([hidden]) {
 #wb-settings-lang-widget {
 	vertical-align: top;
 	margin-left: 5px;
-}
-#login-button {
-	display: flex;
-	margin: 20px auto;
-	padding: 20px 50px;
-	width: -moz-fit-content;
-	width: fit-content;
-	justify-content: center;
-	align-items: center;
-	font-size: 300%;
-	background: #2f3136;
-	border: 5px solid #202225;
-	border-radius: 30px;
-}
-#login-button:hover {
-	background-color:#36393f;
-}
-#login-button img {
-	width: 60px;
-	height: 60px;
-	padding-right: 10px;
 }

+ 21 - 0
dashboard/src/lang.js

@@ -1,3 +1,24 @@
+var currentTheme = ( document.cookie.split('; ').find( cookie => {
+	return cookie.split('=')[0] === 'theme' && /^"(?:light|dark)"$/.test(( cookie.split('=')[1] || '' ));
+} ) || 'dark' ).replace( /^theme="(light|dark)"$/, '$1' );
+var lightTheme = document.getElementById('theme-light');
+var darkTheme = document.getElementById('theme-dark');
+lightTheme.onclick = function() {
+	document.cookie = 'theme="light"; Path=/; Max-Age=31536000';
+	document.documentElement.classList.add('theme-light');
+	lightTheme.setAttribute('style', 'display: none;');
+	darkTheme.removeAttribute('style');
+};
+darkTheme.onclick = function() {
+	document.cookie = 'theme="dark"; Path=/; Max-Age=31536000';
+	document.documentElement.classList.remove('theme-light');
+	darkTheme.setAttribute('style', 'display: none;');
+	lightTheme.removeAttribute('style');
+};
+document.getElementById('theme-separator').removeAttribute('style');
+if ( currentTheme === 'light' ) darkTheme.removeAttribute('style');
+else lightTheme.removeAttribute('style');
+
 var currentLang = ( document.cookie.split('; ').find( cookie => {
 	return cookie.split('=')[0] === 'language' && /^"[a-z\-]+"$/.test(( cookie.split('=')[1] || '' ));
 } ) || 'en' ).replace( /^language="([a-z\-]+)"$/, '$1' );