feat(ui): enhance UI elements and improve layout for game creation and rules pages
This commit is contained in:
@@ -1,6 +1,13 @@
|
|||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
:root {
|
:root {
|
||||||
--background-image: url('/assets/images/background.png');
|
--background-image: url('/assets/images/background.png');
|
||||||
--color: white;
|
--color: #f8f9fa; /* Light text on dark bg */
|
||||||
|
|
||||||
|
/* Bootstrap variable overrides for dark mode */
|
||||||
|
--bs-body-color: var(--color);
|
||||||
|
--bs-link-color: #66b2ff;
|
||||||
|
--bs-link-hover-color: #99ccff;
|
||||||
|
--bs-border-color: rgba(255, 255, 255, 0.2);
|
||||||
|
--bs-heading-color: var(--color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,19 @@
|
|||||||
@import "dark-mode.less";
|
@import "dark-mode.less";
|
||||||
@import "login.less";
|
@import "login.less";
|
||||||
|
|
||||||
|
/* Provide default (light) variables so the site works even if light-mode.less fails */
|
||||||
|
:root {
|
||||||
|
--background-image: url('/assets/images/img.png');
|
||||||
|
--color: #212529; /* Bootstrap body text default */
|
||||||
|
|
||||||
|
/* Bootstrap variable overrides for light mode */
|
||||||
|
--bs-body-color: var(--color) !important;
|
||||||
|
--bs-link-color: #0d6efd !important;
|
||||||
|
--bs-link-hover-color: #0a58ca !important;
|
||||||
|
--bs-border-color: rgba(0, 0, 0, 0.125) !important;
|
||||||
|
--bs-heading-color: var(--color) !important;
|
||||||
|
}
|
||||||
|
|
||||||
@background-image: var(--background-image);
|
@background-image: var(--background-image);
|
||||||
@color: var(--color);
|
@color: var(--color);
|
||||||
@keyframes slideIn {
|
@keyframes slideIn {
|
||||||
@@ -10,8 +23,33 @@
|
|||||||
}
|
}
|
||||||
.game-field-background {
|
.game-field-background {
|
||||||
background-image: @background-image;
|
background-image: @background-image;
|
||||||
background-size: 100vw 100vh;
|
background-size: cover;
|
||||||
|
background-position: center center;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
|
background-attachment: fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-header{
|
||||||
|
text-align:center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-toggle {
|
||||||
|
float: none;
|
||||||
|
margin-right:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure body text color follows theme variable and works with Bootstrap */
|
||||||
|
body {
|
||||||
|
color: @color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 12px;
|
||||||
|
color: @color;
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
flex-grow: 1; /* fill remaining vertical space as visual footer background */
|
||||||
}
|
}
|
||||||
|
|
||||||
.game-field {
|
.game-field {
|
||||||
@@ -19,6 +57,7 @@
|
|||||||
inset: 0;
|
inset: 0;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
#sessions {
|
#sessions {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@@ -61,9 +61,7 @@ class MainMenuController @Inject()(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def rules(): Action[AnyContent] = {
|
def rules(): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
||||||
Action { implicit request =>
|
Ok(views.html.mainmenu.rules(Some(request.user)))
|
||||||
Ok(views.html.mainmenu.rules())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,6 +22,12 @@ class StubUserManager @Inject()(val config: Config) extends UserManager {
|
|||||||
id = java.util.UUID.fromString("223e4567-e89b-12d3-a456-426614174000"),
|
id = java.util.UUID.fromString("223e4567-e89b-12d3-a456-426614174000"),
|
||||||
name = "Leon",
|
name = "Leon",
|
||||||
passwordHash = UserHash.hashPW("password123")
|
passwordHash = UserHash.hashPW("password123")
|
||||||
|
),
|
||||||
|
"Jakob" -> User(
|
||||||
|
internalId = 2L,
|
||||||
|
id = java.util.UUID.fromString("323e4567-e89b-12d3-a456-426614174000"),
|
||||||
|
name = "Jakob",
|
||||||
|
passwordHash = UserHash.hashPW("password123")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -56,16 +56,16 @@
|
|||||||
|
|
||||||
<!-- Player Hand at the BOTTOM -->
|
<!-- Player Hand at the BOTTOM -->
|
||||||
<div class="row justify-content-center g-2 mt-4">
|
<div class="row justify-content-center g-2 mt-4">
|
||||||
@for(i <- player.currentHand().get.cards.indices) {
|
@for(i <- player.currentHand().get.cards.indices) {
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
<form action="@(routes.IngameController.playCard(gamelobby.id))" method="post" class="m-0 p-0">
|
<form action="@(routes.IngameController.playCard(gamelobby.id))" method="post" class="m-0 p-0">
|
||||||
<input type="hidden" name="cardId" value="@i" />
|
<input type="hidden" name="cardId" value="@i" />
|
||||||
<button type="submit" class="btn btn-outline-light p-0 border-0 shadow-none">
|
<button type="submit" class="btn btn-outline-light p-0 border-0 shadow-none">
|
||||||
@util.WebUIUtils.cardtoImage(player.currentHand().get.cards(i)) width="120px"/>
|
@util.WebUIUtils.cardtoImage(player.currentHand().get.cards(i)) width="120px"/>
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,21 @@
|
|||||||
@()
|
@()
|
||||||
|
|
||||||
@main("Login") {
|
@main("Login") {
|
||||||
|
|
||||||
<div class="login-box">
|
<div class="login-box">
|
||||||
<div class="card login-card p-4">
|
<div class="card login-card p-4">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h3 class="text-center mb-4">Login</h3>
|
<h3 class="text-center mb-4 text-body">Login</h3>
|
||||||
|
|
||||||
<form action="@routes.UserController.login_Post()" method="post">
|
<form action="@routes.UserController.login_Post()" method="post">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="username" class="form-label">Username</label>
|
<label for="username" class="form-label text-body">Username</label>
|
||||||
<input type="text" class="form-control" id="username" name="username" placeholder="Enter Username" required>
|
<input type="text" class="form-control text-body" id="username" name="username" placeholder="Enter Username" required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="password" class="form-label">Password</label>
|
<label for="password" class="form-label text-body">Password</label>
|
||||||
<input type="password" class="form-control" id="password" name="password" placeholder="Enter password" required>
|
<input type="password" class="form-control text-body" id="password" name="password" placeholder="Enter password" required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
@@ -34,6 +35,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="@routes.Assets.versioned("/javascripts/particles.js")"></script>
|
<script src="@routes.Assets.versioned("/javascripts/particles.js")"></script>
|
||||||
|
<script>
|
||||||
|
particlesJS.load('particles-js', 'assets/conf/particlesjs-config.json', function() {
|
||||||
|
console.log('callback - particles.js config loaded');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
<div id="particles-js" style="background-color: rgb(182, 25, 36);
|
<div id="particles-js" style="background-color: rgb(182, 25, 36);
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
|
|||||||
@@ -3,24 +3,30 @@
|
|||||||
* handles the rendering of the page header and body tags. It takes
|
* handles the rendering of the page header and body tags. It takes
|
||||||
* two arguments, a `String` for the title of the page and an `Html`
|
* two arguments, a `String` for the title of the page and an `Html`
|
||||||
* object to insert into the body of the page.
|
* object to insert into the body of the page.
|
||||||
*@
|
*@
|
||||||
@(title: String)(content: Html)
|
@(title: String)(content: Html)
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
@* Here's where we render the page title `String`. *@
|
@* Here's where we render the page title `String`. *@
|
||||||
<title>@title</title>
|
<title>@title</title>
|
||||||
<link rel="stylesheet" media="screen" href="@routes.Assets.versioned("stylesheets/main.css")">
|
<link rel="stylesheet" media="screen" href="@routes.Assets.versioned("stylesheets/main.css")">
|
||||||
<link rel="shortcut icon" type="image/png" href="@routes.Assets.versioned("images/favicon.png")">
|
<link rel="shortcut icon" type="image/png" href="@routes.Assets.versioned("images/favicon.png")">
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous">
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body class="d-flex flex-column min-vh-100 game-field-background">
|
||||||
@* And here's where we render the `Html` object containing
|
<main>
|
||||||
* the page content. *@
|
@* And here's where we render the `Html` object containing
|
||||||
@content
|
* the page content. *@
|
||||||
|
@content
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer class="footer">
|
||||||
|
</footer>
|
||||||
|
|
||||||
<script src="@routes.Assets.versioned("javascripts/main.js")" type="text/javascript"></script>
|
<script src="@routes.Assets.versioned("javascripts/main.js")" type="text/javascript"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@@5.3.8/dist/js/bootstrap.bundle.min.js" integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI" crossorigin="anonymous"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@@5.3.8/dist/js/bootstrap.bundle.min.js" integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI" crossorigin="anonymous"></script>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
@main("Create Game") {
|
@main("Create Game") {
|
||||||
@navbar(user)
|
@navbar(user)
|
||||||
<form action="@routes.MainMenuController.createGame()" method="post">
|
<form action="@routes.MainMenuController.createGame()" method="post" class="game-field-background">
|
||||||
<div class="w-50 mx-auto">
|
<div class="w-50 mx-auto">
|
||||||
<div class="mt-3">
|
<div class="mt-3">
|
||||||
<label for="lobbyname" class="form-label">Lobby-Name</label>
|
<label for="lobbyname" class="form-label">Lobby-Name</label>
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="mt-3">
|
<div class="mt-3">
|
||||||
<label for="playeramount" class="form-label">Playeramount:</label>
|
<label for="playeramount" class="form-label">Playeramount:</label>
|
||||||
<input type="range" class="form-range" min="2" max="7" value="2" id="playeramount" name="playeramount">
|
<input type="range" class="form-range text-body" min="2" max="7" value="2" id="playeramount" name="playeramount">
|
||||||
<div class="d-flex justify-content-between">
|
<div class="d-flex justify-content-between">
|
||||||
<span>2</span>
|
<span>2</span>
|
||||||
<span>3</span>
|
<span>3</span>
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
@(user: Option[model.users.User])
|
@(user: Option[model.users.User])
|
||||||
<nav class="navbar navbar-expand-lg bg-body-tertiary">
|
<nav class="navbar navbar-expand-lg bg-body-tertiary">
|
||||||
<div class="container-fluid">
|
<div class="container d-flex justify-content-center">
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navBar" aria-controls="navBar" aria-expanded="false" aria-label="Toggle navigation">
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navBar" aria-controls="navBar" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
<div class="collapse navbar-collapse" id="navBar">
|
<div class="collapse navbar-collapse justify-content-center" id="navBar">
|
||||||
<a class="navbar-brand" href="@routes.MainMenuController.mainMenu()">KnockOutWhist</a>
|
<a class="navbar-brand ms-auto" href="@routes.MainMenuController.mainMenu()">KnockOutWhist</a>
|
||||||
<div class="navbar-nav me-auto mb-2 mb-lg-0">
|
<div class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||||
<ul class="navbar-nav mb-2 mb-lg-0">
|
<ul class="navbar-nav mb-2 mb-lg-0">
|
||||||
@if(user.isDefined) {
|
@if(user.isDefined) {
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link active" aria-current="page" href="#">Create Game</a>
|
<a class="nav-link active" aria-current="page" href="@routes.MainMenuController.mainMenu()">Create Game</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link disabled" aria-disabled="true">Lobbies</a>
|
<a class="nav-link disabled" aria-disabled="true">Lobbies</a>
|
||||||
|
|||||||
@@ -1,63 +1,76 @@
|
|||||||
@()
|
@(user: Option[model.users.User])
|
||||||
|
|
||||||
@main("Rules") {
|
@main("Rules") {
|
||||||
<div id="rules" class="game-field game-field-background">
|
@navbar(user)
|
||||||
<table>
|
<div id="rules">
|
||||||
<caption>Rules Overview and Equipment</caption>
|
<div class="container my-4">
|
||||||
<thead>
|
<div class="card shadow-sm rounded-3">
|
||||||
<tr>
|
<div class="card-header text-white text-center">
|
||||||
<th>Section</th>
|
<h4 class="mb-0 text-body">Game Rules Overview</h4>
|
||||||
<th>Details</th>
|
</div>
|
||||||
</tr>
|
<div class="card-body p-0">
|
||||||
</thead>
|
<div class="table-responsive">
|
||||||
<tbody>
|
<table class="table table-striped table-hover mb-0 align-middle">
|
||||||
<tr>
|
<thead class="table-dark">
|
||||||
<td>Players</td>
|
<tr>
|
||||||
<td>Two to seven players. The aim is to be the last player left in the game.</td>
|
<th scope="col">Section</th>
|
||||||
</tr>
|
<th scope="col">Details</th>
|
||||||
<tr>
|
</tr>
|
||||||
<td>Aim</td>
|
</thead>
|
||||||
<td>To be the last player left in at the end of the game, with the object in each hand being to win a majority of tricks.</td>
|
<tbody>
|
||||||
</tr>
|
<tr>
|
||||||
<tr>
|
<td>Players</td>
|
||||||
<td>Equipment</td>
|
<td>Two to seven players. The aim is to be the last player left in the game.</td>
|
||||||
<td>A standard 52-card pack is used.</td>
|
</tr>
|
||||||
</tr>
|
<tr>
|
||||||
<tr>
|
<td>Aim</td>
|
||||||
<td>Card Ranks</td>
|
<td>To be the last player left at the end of the game, with the object in each hand being to win a majority of tricks.</td>
|
||||||
<td>In each suit, cards rank from highest to lowest: A K Q J 10 9 8 7 6 5 4 3 2.</td>
|
</tr>
|
||||||
</tr>
|
<tr>
|
||||||
<tr>
|
<td>Equipment</td>
|
||||||
<td>Deal (First Hand)</td>
|
<td>A standard 52-card pack is used.</td>
|
||||||
<td>The dealer deals seven cards to each player. One card is turned up to determine the trump suit for the round.</td>
|
</tr>
|
||||||
</tr>
|
<tr>
|
||||||
<tr>
|
<td>Card Ranks</td>
|
||||||
<td>Deal (Subsequent Hands)</td>
|
<td>In each suit, cards rank from highest to lowest: A K Q J 10 9 8 7 6 5 4 3 2.</td>
|
||||||
<td>The deal rotates clockwise. The player who took the most tricks in the previous hand selects the trump suit. If there's a tie for the highest number of tricks, players cut cards to decide who calls trumps. One fewer card is dealt in each successive hand until the final hand consists of one card each.</td>
|
</tr>
|
||||||
</tr>
|
<tr>
|
||||||
<tr>
|
<td>Deal (First Hand)</td>
|
||||||
<td>Play</td>
|
<td>The dealer deals seven cards to each player. One card is turned up to determine the trump suit for the round.</td>
|
||||||
<td>The player to the dealer's left (eldest hand) leads the first trick. Any card can be led. Other players must follow suit if possible. A player with no cards of the suit led may play any card.</td>
|
</tr>
|
||||||
</tr>
|
<tr>
|
||||||
<tr>
|
<td>Deal (Subsequent Hands)</td>
|
||||||
<td>Winning a Trick</td>
|
<td>The deal rotates clockwise. The player who took the most tricks in the previous hand selects the trump suit. If there's a tie, players cut cards to decide who calls trumps. One fewer card is dealt in each successive hand until the final hand consists of one card each.</td>
|
||||||
<td>The highest card of the suit led wins, unless a trump is played, in which case the highest trump wins. The winner of the trick leads the next.</td>
|
</tr>
|
||||||
</tr>
|
<tr>
|
||||||
<tr>
|
<td>Play</td>
|
||||||
<td>Leading Trumps</td>
|
<td>The player to the dealer's left (eldest hand) leads the first trick. Any card can be led. Other players must follow suit if possible. A player with no cards of the suit led may play any card.</td>
|
||||||
<td>Some rules disallow leading trumps before the suit has been 'broken' (a trump has been played to the lead of another suit). Leading trumps is always permissible if a player holds only trumps.</td>
|
</tr>
|
||||||
</tr>
|
<tr>
|
||||||
<tr>
|
<td>Winning a Trick</td>
|
||||||
<td>Knockout</td>
|
<td>The highest card of the suit led wins, unless a trump is played, in which case the highest trump wins. The winner of the trick leads the next.</td>
|
||||||
<td>At the end of each hand, any player who took no tricks is knocked out and takes no further part in the game.</td>
|
</tr>
|
||||||
</tr>
|
<tr>
|
||||||
<tr>
|
<td>Leading Trumps</td>
|
||||||
<td>Winning the Game</td>
|
<td>Some rules disallow leading trumps before the suit has been 'broken' (a trump has been played to the lead of another suit). Leading trumps is always permissible if a player holds only trumps.</td>
|
||||||
<td>The game is won when a player takes all the tricks in a round, as all other players are knocked out, leaving only one player remaining.</td>
|
</tr>
|
||||||
</tr>
|
<tr>
|
||||||
</tbody>
|
<td>Knockout</td>
|
||||||
<td>Dog Life</td>
|
<td>At the end of each hand, any player who took no tricks is knocked out and takes no further part in the game.</td>
|
||||||
<td>The first player who takes no tricks is awarded a "dog's life". In the next hand, that player is dealt one card and can decide which trick to play it to. Each time a trick is played the "dog" may either play the card or knock on the table and wait to play it later. If the dog wins a trick, the player to the left leads to the next and the dog re-enters the game properly in the next hand. If the dog fails, they are knocked out.</td>
|
</tr>
|
||||||
</table>
|
<tr>
|
||||||
|
<td>Winning the Game</td>
|
||||||
|
<td>The game is won when a player takes all the tricks in a round, as all other players are knocked out, leaving only one player remaining.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Dog Life</td>
|
||||||
|
<td>The first player who takes no tricks is awarded a "dog's life". In the next hand, that player is dealt one card and can decide which trick to play it to. Each time a trick is played the "dog" may either play the card or knock on the table and wait to play it later. If the dog wins a trick, the player to the left leads the next and the dog re-enters the game properly in the next hand. If the dog fails, they are knocked out.</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,80 @@
|
|||||||
particlesJS.load('particles-js', 'assets/conf/particlesjs-config.json', function() {
|
/*!
|
||||||
console.log('callback - particles.js config loaded');
|
* Color mode toggler for Bootstrap's docs (https://getbootstrap.com/)
|
||||||
});
|
* Copyright 2011-2025 The Bootstrap Authors
|
||||||
|
* Licensed under the Creative Commons Attribution 3.0 Unported License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(() => {
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const getStoredTheme = () => localStorage.getItem('theme')
|
||||||
|
const setStoredTheme = theme => localStorage.setItem('theme', theme)
|
||||||
|
|
||||||
|
const getPreferredTheme = () => {
|
||||||
|
const storedTheme = getStoredTheme()
|
||||||
|
if (storedTheme) {
|
||||||
|
return storedTheme
|
||||||
|
}
|
||||||
|
|
||||||
|
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
||||||
|
}
|
||||||
|
|
||||||
|
const setTheme = theme => {
|
||||||
|
if (theme === 'auto') {
|
||||||
|
document.documentElement.setAttribute('data-bs-theme', (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'))
|
||||||
|
} else {
|
||||||
|
document.documentElement.setAttribute('data-bs-theme', theme)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setTheme(getPreferredTheme())
|
||||||
|
|
||||||
|
const showActiveTheme = (theme, focus = false) => {
|
||||||
|
const themeSwitcher = document.querySelector('#bd-theme')
|
||||||
|
|
||||||
|
if (!themeSwitcher) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const themeSwitcherText = document.querySelector('#bd-theme-text')
|
||||||
|
const activeThemeIcon = document.querySelector('.theme-icon-active use')
|
||||||
|
const btnToActive = document.querySelector(`[data-bs-theme-value="${theme}"]`)
|
||||||
|
const svgOfActiveBtn = btnToActive.querySelector('svg use').getAttribute('href')
|
||||||
|
|
||||||
|
document.querySelectorAll('[data-bs-theme-value]').forEach(element => {
|
||||||
|
element.classList.remove('active')
|
||||||
|
element.setAttribute('aria-pressed', 'false')
|
||||||
|
})
|
||||||
|
|
||||||
|
btnToActive.classList.add('active')
|
||||||
|
btnToActive.setAttribute('aria-pressed', 'true')
|
||||||
|
activeThemeIcon.setAttribute('href', svgOfActiveBtn)
|
||||||
|
const themeSwitcherLabel = `${themeSwitcherText.textContent} (${btnToActive.dataset.bsThemeValue})`
|
||||||
|
themeSwitcher.setAttribute('aria-label', themeSwitcherLabel)
|
||||||
|
|
||||||
|
if (focus) {
|
||||||
|
themeSwitcher.focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
|
||||||
|
const storedTheme = getStoredTheme()
|
||||||
|
if (storedTheme !== 'light' && storedTheme !== 'dark') {
|
||||||
|
setTheme(getPreferredTheme())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
window.addEventListener('DOMContentLoaded', () => {
|
||||||
|
showActiveTheme(getPreferredTheme())
|
||||||
|
|
||||||
|
document.querySelectorAll('[data-bs-theme-value]')
|
||||||
|
.forEach(toggle => {
|
||||||
|
toggle.addEventListener('click', () => {
|
||||||
|
const theme = toggle.getAttribute('data-bs-theme-value')
|
||||||
|
setStoredTheme(theme)
|
||||||
|
setTheme(theme)
|
||||||
|
showActiveTheme(theme, true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})()
|
||||||
Reference in New Issue
Block a user