Bläddra i källkod

Migrate pages to use tailwind

Daniel Bohry 9 månader sedan
förälder
incheckning
70640530f2

+ 1 - 1
.env

@@ -1,2 +1,2 @@
 VITE_AUTH_HOST=https://auth.lhamacorp.com
-VITE_STOCKS_HOST=https://stocks-be.lhamacorp.com
+VITE_STOCKS_HOST=http://localhost:8080

+ 4 - 1
package.json

@@ -27,6 +27,9 @@
 	},
 	"type": "module",
 	"dependencies": {
-		"chart.js": "^4.4.8"
+		"@tailwindcss/postcss": "^4.1.4",
+		"chart.js": "^4.4.8",
+		"postcss": "^8.5.3",
+		"tailwindcss": "^4.1.4"
 	}
 }

+ 5 - 0
postcss.config.js

@@ -0,0 +1,5 @@
+export default {
+	plugins: {
+		"@tailwindcss/postcss": {},
+	}
+}

+ 1 - 102
src/app.css

@@ -1,103 +1,2 @@
+@import "tailwindcss";
 @import '@fontsource/fira-mono';
-
-:root {
-	--font-body: 'Roboto', Arial, AppleGothic, serif;
-	--font-mono: 'Fira Mono', monospace;
-	--color-bg-0: rgb(202, 216, 228);
-	--color-bg-1: hsl(209, 36%, 86%);
-	--color-bg-2: hsl(224, 44%, 95%);
-	--color-theme-1: #4b6584;
-	--color-theme-2: #4075a6;
-	--color-text: rgba(0, 0, 0, 0.7);
-	--column-width: 42rem;
-	--column-margin-top: 4rem;
-	font-family: var(--font-body);
-	color: var(--color-text);
-}
-
-body {
-	min-height: 100vh;
-	margin: 0;
-	background-attachment: fixed;
-	background-color: var(--color-bg-1);
-	background-size: 100vw 100vh;
-	background-image:
-		radial-gradient(50% 50% at 50% 50%, rgba(255, 255, 255, 0.75) 0%, rgba(255, 255, 255, 0) 100%),
-		linear-gradient(180deg, var(--color-bg-0) 0%, var(--color-bg-1) 15%, var(--color-bg-2) 50%);
-}
-
-h1,
-h2,
-p {
-	font-weight: 400;
-}
-
-p {
-	line-height: 1.5;
-}
-
-a {
-	color: var(--color-theme-1);
-	text-decoration: none;
-}
-
-a:hover {
-	text-decoration: underline;
-}
-
-h1 {
-	font-size: 2rem;
-	text-align: center;
-}
-
-h2 {
-	font-size: 1rem;
-}
-
-pre {
-	font-size: 16px;
-	font-family: var(--font-mono);
-	background-color: rgba(255, 255, 255, 0.45);
-	border-radius: 3px;
-	box-shadow: 2px 2px 6px rgb(255 255 255 / 25%);
-	padding: 0.5em;
-	overflow-x: auto;
-	color: var(--color-text);
-}
-
-.text-column {
-	display: flex;
-	max-width: 48rem;
-	flex: 0.6;
-	flex-direction: column;
-	justify-content: center;
-	margin: 0 auto;
-}
-
-input,
-button {
-	font-size: inherit;
-	font-family: inherit;
-}
-
-button:focus:not(:focus-visible) {
-	outline: none;
-}
-
-@media (min-width: 720px) {
-	h1 {
-		font-size: 2.4rem;
-	}
-}
-
-.visually-hidden {
-	border: 0;
-	clip: rect(0 0 0 0);
-	height: auto;
-	margin: 0;
-	overflow: hidden;
-	padding: 0;
-	position: absolute;
-	width: 1px;
-	white-space: nowrap;
-}

+ 0 - 2
src/app.html

@@ -7,8 +7,6 @@
 			href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap"
 			rel="stylesheet"
 		/>
-		<link rel="stylesheet" href="./css/main.css" />
-		<link rel="stylesheet" href="./css/form.css" />
 		<meta name="viewport" content="width=device-width, initial-scale=1" />
 		%sveltekit.head%
 	</head>

+ 42 - 138
src/components/AddStock.svelte

@@ -17,154 +17,58 @@
 </script>
 
 {#if show}
-	<div class="modal-container">
-		<div class="modal-content">
-			<form on:submit|preventDefault={handleSubmit}>
-				<div class="row">
-					<div class="col">
+	<!-- Modal backdrop -->
+	<div class="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50">
+		<!-- Modal content -->
+		<div class="bg-white dark:bg-gray-900 rounded-xl shadow-lg p-6 w-full max-w-xl mx-4 space-y-6">
+			<form on:submit|preventDefault={handleSubmit} class="space-y-4">
+				<div class="flex flex-col sm:flex-row gap-4">
+					<input
+						type="text"
+						class="flex-1 px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
+						placeholder="Stock code or name"
+						bind:value={stockCode}
+						on:input={() => (stockCode = stockCode.toUpperCase())}
+						autocomplete="off"
+						autofocus
+					/>
+
+					<div class="flex gap-2">
 						<input
-							type="text"
-							class="form-control"
-							placeholder="stock code or name"
-							bind:value={stockCode}
-							on:input={() => (stockCode = stockCode.toUpperCase())}
-							autocomplete="off"
-							autofocus
+							type="reset"
+							value="Cancel"
+							class="px-4 py-2 bg-gray-300 hover:bg-gray-400 text-gray-800 rounded-lg cursor-pointer transition"
+							on:click={onClose}
+						/>
+
+						<input
+							type="submit"
+							value="Search"
+							class="px-4 py-2 bg-blue-500 hover:bg-blue-600 text-white rounded-lg cursor-pointer transition"
 						/>
-					</div>
-					<div class="col">
-						<input type="reset" value="cancel" class="btn btn-danger" on:click={onClose} />
-						<input type="submit" value="search" class="btn btn-primary" />
 					</div>
 				</div>
 			</form>
 
 			{#if searchResults.length > 0}
-				<div class="modal-result">
-					<div class="card" style="width: 90%;">
-						<ul class="list-group list-group-flush">
-							{#each searchResults as result}
-								<li
-									class="list-group-item d-flex justify-content-between align-items-center"
-									on:click={() => onAddStock(result)}
-								>
+				<div class="mt-4 space-y-2">
+					<ul class="divide-y divide-gray-200 dark:divide-gray-700 bg-white dark:bg-gray-800 rounded-lg shadow-sm overflow-hidden">
+						{#each searchResults as result}
+							<li
+								class="flex items-center justify-between px-4 py-3 hover:bg-gray-100 dark:hover:bg-gray-700 cursor-pointer"
+								on:click={() => onAddStock(result)}
+							>
+								<span class="text-gray-800 dark:text-gray-100 text-sm">
 									({result.code}) {result.name}
-									<button class="btn btn-primary btn-sm">+</button>
-								</li>
-							{/each}
-						</ul>
-					</div>
+								</span>
+								<button class="bg-blue-500 hover:bg-blue-600 text-white px-3 py-1 text-sm rounded-md transition">
+									+
+								</button>
+							</li>
+						{/each}
+					</ul>
 				</div>
 			{/if}
 		</div>
 	</div>
 {/if}
-
-<style>
-	.modal-container {
-		position: fixed;
-		top: 0;
-		left: 0;
-		width: 100%;
-		height: 100%;
-		background-color: rgba(0, 0, 0, 0.5);
-		display: flex;
-		align-items: center;
-		justify-content: center;
-		z-index: 1000;
-	}
-
-	.modal-content {
-		background-color: white;
-		border-radius: 12px;
-		padding: 2rem;
-		width: 100%;
-		max-width: 500px;
-		animation: fadeIn 0.3s ease-in-out;
-	}
-
-	form {
-		display: flex;
-		flex-direction: column;
-		gap: 1rem;
-	}
-
-	.row {
-		display: flex;
-		flex-wrap: wrap;
-		gap: 1rem;
-	}
-
-	.col {
-		flex: 1;
-	}
-
-	input[type='text'] {
-		width: 70%;
-		padding: 0.75rem 1rem;
-		border: 1px solid #ccc;
-		border-radius: 8px;
-		font-size: 1rem;
-		outline: none;
-		transition: border 0.2s;
-	}
-
-	input[type='text']:focus {
-		border-color: #007bff;
-	}
-
-	.btn {
-		padding: 0.6rem 1rem;
-		font-size: 1rem;
-		border: none;
-		border-radius: 8px;
-		cursor: pointer;
-		transition: background 0.2s ease-in-out;
-	}
-
-	.btn-primary {
-		background-color: #007bff;
-		color: white;
-	}
-
-	.btn-primary:hover {
-		background-color: #0056b3;
-	}
-
-	.btn-danger {
-		background-color: #dc3545;
-		color: white;
-	}
-
-	.btn-danger:hover {
-		background-color: #b02a37;
-	}
-
-	.modal-result {
-		margin-top: 1.5rem;
-	}
-
-	.list-group-item {
-		cursor: pointer;
-		transition: background 0.2s;
-	}
-
-	.list-group-item:hover {
-		background-color: #f1f1f1;
-	}
-
-	.list-group-item button {
-		margin-left: auto;
-	}
-
-	@keyframes fadeIn {
-		from {
-			opacity: 0;
-			transform: translateY(-20px);
-		}
-		to {
-			opacity: 1;
-			transform: translateY(0);
-		}
-	}
-</style>

+ 18 - 30
src/components/CurrentPositionChart.svelte

@@ -31,7 +31,12 @@
 			ctx.save();
 
 			ctx.font = 'bold 20px sans-serif';
-			ctx.fillStyle = '#333';
+
+			// ✅ Dynamically read computed text color from a hidden DOM element
+			const probe = document.getElementById('chart-text-color');
+			const computedColor = getComputedStyle(probe)?.color;
+			ctx.fillStyle = computedColor || '#333';
+
 			ctx.textAlign = 'center';
 			ctx.textBaseline = 'middle';
 
@@ -87,7 +92,7 @@
 				},
 				tooltip: {
 					callbacks: {
-						label: function(tooltipItem) {
+						label: function (tooltipItem) {
 							return `${tooltipItem.label}: ${formatCurrency(tooltipItem.raw, currency)}`;
 						}
 					}
@@ -111,37 +116,20 @@
 			chartInstance.destroy();
 		}
 	});
-
 </script>
 
-<div class="chart-wrapper">
+<!-- 👇 Tailwind container with probe element and canvas -->
+<div class="max-w-sm md:max-w-md lg:max-w-lg mx-auto relative" style="height: 400px;">
 	{#if data && data.stocks && data.stocks.length > 0}
-		<p class="chart-title">Current Portfolio Positions</p>
-		<canvas bind:this={chartContainer}></canvas>
+		<p class="text-center font-semibold text-gray-700 dark:text-gray-300 mb-2 text-base">
+			Current Portfolio Positions
+		</p>
+
+		<!-- 👇 Hidden span used for runtime color detection -->
+		<span id="chart-text-color" class="hidden text-gray-900 dark:text-gray-100"></span>
+
+		<canvas bind:this={chartContainer} class="w-full h-full"></canvas>
 	{:else}
-		<p>No insights available.</p>
+		<p class="text-center text-gray-500 dark:text-gray-400">No insights available.</p>
 	{/if}
 </div>
-
-<style>
-    .chart-wrapper {
-        width: 400px;
-        height: 400px;
-        max-width: 100%;
-        margin: auto;
-        position: relative;
-    }
-
-    canvas {
-        width: 100% !important;
-        height: 100% !important;
-    }
-
-    .chart-title {
-        text-align: center;
-        font-weight: bold;
-        margin-bottom: 0.5rem;
-        font-size: 1rem;
-        color: #333;
-    }
-</style>

+ 59 - 125
src/components/Header.svelte

@@ -4,12 +4,12 @@
 	import { authentication } from '../routes/store.js';
 	import { onMount } from 'svelte';
 
-	let username = undefined;
-	let profileTitle = undefined;
+	let username = '';
+	let profileTitle = 'Login';
 
 	const unsubscribe = authentication.subscribe((value) => {
-		username = value ? value.username : '';
-		profileTitle = value ? `You are logged in as ${value.username}` : 'Login';
+		username = value?.username || '';
+		profileTitle = username ? `You are logged in as ${username}` : 'Login';
 	});
 
 	onMount(() => {
@@ -22,129 +22,63 @@
 			}
 		}
 
-		return () => {
-			unsubscribe();
-		};
+		return () => unsubscribe();
 	});
 </script>
 
-<header>
-	<div class="corner">
-		<!-- logo -->
-	</div>
-
-	<nav>
-		<ul>
-			{#if username !== ''}
-				<li aria-current={$page.url.pathname === '/home' ? 'page' : undefined}>
-					<a href="/home">Home</a>
-				</li>
-				<li aria-current={$page.url.pathname === '/stocks' ? 'page' : undefined}>
-					<a href="/stocks">Stocks</a>
-				</li>
-				<li aria-current={$page.url.pathname === '/portfolio' ? 'page' : undefined}>
-					<a href="/portfolio">Portfolio</a>
-				</li>
-				<li aria-current={$page.url.pathname === '/insights' ? 'page' : undefined}>
-					<a href="/insights">Insights</a>
-				</li>
-			{/if}
-<!--			<li aria-current={$page.url.pathname === '/about' ? 'page' : undefined}>-->
-<!--				<a href="/about">About</a>-->
-<!--			</li>-->
-		</ul>
-	</nav>
-
-	<div class="corner">
-		<a href="/profile">
-		<div class="login-message">{username}</div>
-			<img src={profile} alt="Profile" style="width: 16px; height: 16px;" title={profileTitle} />
-		</a>
+<header class="bg-white dark:bg-gray-900 shadow-md">
+	<div class="max-w-7xl mx-auto flex items-center justify-between px-4 py-3">
+		<!-- Left: logo / placeholder -->
+		<div class="flex items-center">
+			<!-- Insert logo or branding here -->
+			<span class="text-lg font-bold text-theme-light dark:text-theme-dark">OQuokka</span>
+		</div>
+
+		<!-- Center: navigation -->
+		<nav>
+			<ul class="flex gap-6 text-sm font-medium text-gray-700 dark:text-gray-300">
+				{#if username}
+<!--					<li aria-current={$page.url.pathname === '/home' ? 'page' : undefined}>-->
+<!--						<a-->
+<!--							href="/home"-->
+<!--							class="hover:text-theme-light dark:hover:text-theme-dark transition-colors"-->
+<!--						>Home</a>-->
+<!--					</li>-->
+					<li aria-current={$page.url.pathname === '/stocks' ? 'page' : undefined}>
+						<a
+							href="/stocks"
+							class="hover:text-theme-light dark:hover:text-theme-dark transition-colors"
+						>Stocks</a>
+					</li>
+					<li aria-current={$page.url.pathname === '/portfolio' ? 'page' : undefined}>
+						<a
+							href="/portfolio"
+							class="hover:text-theme-light dark:hover:text-theme-dark transition-colors"
+						>Portfolio</a>
+					</li>
+					<li aria-current={$page.url.pathname === '/insights' ? 'page' : undefined}>
+						<a
+							href="/insights"
+							class="hover:text-theme-light dark:hover:text-theme-dark transition-colors"
+						>Insights</a>
+					</li>
+				{/if}
+			</ul>
+		</nav>
+
+		<!-- Right: user profile -->
+		<div class="flex items-center gap-3">
+			<a href="/profile" class="flex items-center gap-2 group">
+				<span class="text-sm text-gray-600 dark:text-gray-300 group-hover:text-theme-light dark:group-hover:text-theme-dark">
+					{username}
+				</span>
+				<img
+					src={profile}
+					alt="Profile"
+					class="w-6 h-6 rounded-full border border-gray-300 dark:border-gray-700"
+					title={profileTitle}
+				/>
+			</a>
+		</div>
 	</div>
 </header>
-
-<style>
-	header {
-		display: flex;
-		justify-content: space-between;
-		background-color: #a5b1c2;
-		width: 100%;
-	}
-
-	.corner {
-		padding-right: 1em;
-		height: 3em;
-		display: flex;
-		align-items: center;
-	}
-
-	.login-message {
-		margin-right: 8px;
-		color: var(--color-text);
-	}
-
-	.corner a {
-		display: flex;
-		align-items: center;
-		justify-content: center;
-		width: 100%;
-		height: 100%;
-	}
-
-	.corner img {
-		width: 2em;
-		height: 2em;
-		object-fit: contain;
-	}
-
-	nav {
-		flex-grow: 1;
-		justify-content: flex-start;
-	}
-
-	ul {
-		padding: 0;
-		margin: 0;
-		height: 3em;
-		display: flex;
-		align-items: center;
-		list-style: none;
-		background: var(--background);
-		background-size: contain;
-	}
-
-	li {
-		position: relative;
-		height: 100%;
-	}
-
-	li[aria-current='page']::before {
-		--size: 6px;
-		content: '';
-		width: 0;
-		height: 0;
-		position: absolute;
-		top: 0;
-		left: calc(50% - var(--size));
-		border: var(--size) solid transparent;
-		border-top: var(--size) solid var(--color-theme-1);
-	}
-
-	nav a {
-		display: flex;
-		height: 100%;
-		align-items: center;
-		padding: 0 0.5rem;
-		color: var(--color-text);
-		font-weight: 700;
-		font-size: 0.8rem;
-		text-transform: uppercase;
-		letter-spacing: 0.1em;
-		text-decoration: none;
-		transition: color 0.2s linear;
-	}
-
-	a:hover {
-		color: var(--color-theme-1);
-	}
-</style>

+ 3 - 40
src/routes/+layout.svelte

@@ -5,51 +5,14 @@
 	let year = new Date().getFullYear();
 </script>
 
-<div class="app">
+<div class="flex flex-col min-h-screen bg-gray-100 dark:bg-gray-950 text-gray-900 dark:text-gray-100">
 	<Header />
 
-	<main>
+	<main class="flex-1 flex flex-col px-4 py-6 w-full max-w-4xl mx-auto box-border">
 		<slot />
 	</main>
 
-	<footer>
+	<footer class="flex flex-col items-center justify-center py-3 text-sm text-gray-500 dark:text-gray-400">
 		<p>© {year} OQuokka</p>
 	</footer>
 </div>
-
-<style>
-	.app {
-		display: flex;
-		flex-direction: column;
-		min-height: 100vh;
-	}
-
-	main {
-		flex: 1;
-		display: flex;
-		flex-direction: column;
-		padding: 1rem;
-		width: 100%;
-		max-width: 64rem;
-		margin: 0 auto;
-		box-sizing: border-box;
-	}
-
-	footer {
-		display: flex;
-		flex-direction: column;
-		justify-content: center;
-		align-items: center;
-		padding: 12px;
-	}
-
-	footer a {
-		font-weight: bold;
-	}
-
-	@media (min-width: 480px) {
-		footer {
-			padding: 12px 0;
-		}
-	}
-</style>

+ 1 - 1
src/routes/+page.server.js

@@ -1,5 +1,5 @@
 import { redirect } from '@sveltejs/kit';
 
 export function load() {
-	throw redirect(307, '/home');
+	throw redirect(307, '/stocks');
 }

+ 1 - 1
src/routes/+page.svelte

@@ -1,5 +1,5 @@
 <script>
 	import { goto } from '$app/navigation';
 
-	goto('/home');
+	goto('/stocks');
 </script>

+ 9 - 30
src/routes/insights/+page.svelte

@@ -78,42 +78,21 @@
 </svelte:head>
 
 {#if isLoading}
-	<div in:fade>Loading...</div>
+	<div in:fade class="text-center py-6 text-gray-500 dark:text-gray-300">Loading...</div>
 {:else}
-	<div in:fade>
-		<div class="button-container">
-			<select class="form-control order-select" on:change={updateCurrency} value={currency}>
+	<div in:fade class="space-y-6">
+		<div class="flex justify-end mb-4">
+			<select
+				class="w-52 px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-400"
+				on:change={updateCurrency}
+				bind:value={currency}
+			>
 				<option value="BRL">BRL</option>
 				<option value="EUR">EUR</option>
-				<option value="USD" selected>USD</option>
+				<option value="USD">USD</option>
 			</select>
 		</div>
 
 		<CurrentPositionChart {data} {currency} {total} />
 	</div>
 {/if}
-
-<style>
-    .button-container {
-        display: flex;
-        gap: 10px;
-        margin-bottom: 1rem;
-        align-items: center;
-        justify-content: flex-end;
-    }
-
-    .order-select {
-        width: 200px;
-        padding: 0.5rem;
-        border-radius: 8px;
-        border: 1px solid #ddd;
-        font-size: 1rem;
-        background-color: #f9f9f9;
-        transition: border-color 0.3s ease;
-    }
-
-    .order-select:focus {
-        border-color: #2980b9;
-        outline: none;
-    }
-</style>

+ 37 - 67
src/routes/login/+page.svelte

@@ -87,22 +87,32 @@
 	<meta name="description" content="Login page" />
 </svelte:head>
 
-<div in:fade class="text-column">
+<div in:fade class="flex justify-center items-center min-h-[70vh] px-4">
 	{#if isLoading}
-		<p>Loading...</p>
+		<p class="text-gray-500 dark:text-gray-400">Loading...</p>
 	{:else if !isAuthenticated}
-		<div class="card">
-			<form on:submit={submit}>
-				<input type="text" name="username" placeholder="username" class="input-field" />
-
-				<div class="password-container">
+		<div class="w-full max-w-sm bg-white dark:bg-gray-900 shadow-md rounded-xl p-6 space-y-6">
+			<form on:submit={submit} class="space-y-4">
+				<!-- Username -->
+				<input
+					type="text"
+					name="username"
+					placeholder="Username"
+					class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
+				/>
+
+				<!-- Password -->
+				<div class="relative">
 					<input
 						type={showPassword ? 'text' : 'password'}
 						name="password"
-						placeholder="password"
-						class="input-field"
+						placeholder="Password"
+						class="w-full px-4 py-2 pr-10 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
 					/>
-					<span class="eye-icon" on:click={togglePasswordVisibility}>
+					<span
+						class="absolute inset-y-0 right-3 flex items-center cursor-pointer text-gray-500 dark:text-gray-400 text-lg"
+						on:click={togglePasswordVisibility}
+					>
 						{#if showPassword}
 							👁️‍🗨️
 						{:else}
@@ -111,64 +121,24 @@
 					</span>
 				</div>
 
-				<button type="submit" class="add-button" disabled={isDisabled}>Login</button>
-				<button type="button" class="register-button" on:click={navigateToRegister} disabled={isDisabled}>Register</button>
+				<!-- Buttons -->
+				<button
+					type="submit"
+					class="w-full bg-blue-500 hover:bg-blue-600 text-white py-2 rounded-lg transition"
+					disabled={isDisabled}
+				>
+					Login
+				</button>
+
+				<button
+					type="button"
+					class="w-full bg-gray-300 hover:bg-gray-400 text-gray-800 py-2 rounded-lg transition"
+					on:click={navigateToRegister}
+					disabled={isDisabled}
+				>
+					Register
+				</button>
 			</form>
 		</div>
 	{/if}
 </div>
-
-<style>
-    input::placeholder {
-        opacity: 0.5;
-    }
-
-    .password-container {
-        position: relative;
-    }
-
-    .eye-icon {
-        position: absolute;
-        right: 10px;
-        top: 50%;
-        transform: translateY(-50%);
-        cursor: pointer;
-        font-size: 1.1rem;
-        color: #666;
-        user-select: none;
-    }
-
-    .input-field {
-        width: 100%;
-        padding: 0.5rem 2.5rem 0.5rem 0.5rem;
-        margin-bottom: 1rem;
-        box-sizing: border-box;
-    }
-
-    .add-button,
-    .register-button {
-        padding: 0.5rem 1rem;
-        margin-top: 0.5rem;
-        border: none;
-        border-radius: 4px;
-        cursor: pointer;
-        font-weight: bold;
-    }
-
-    .add-button {
-        background-color: #4caf50;
-        color: white;
-        margin-right: 0.5rem;
-    }
-
-    .register-button {
-        background-color: #2196f3;
-        color: white;
-    }
-
-    button:disabled {
-        pointer-events: none;
-        opacity: 0.6;
-        cursor: not-allowed;
-    }
-</style>

+ 55 - 349
src/routes/portfolio/+page.svelte

@@ -298,28 +298,34 @@
 </svelte:head>
 
 {#if isLoading}
-	<div in:fade>Loading...</div>
+	<div in:fade class="text-center py-6 text-gray-500 dark:text-gray-300">Loading...</div>
 {:else if portfolioId}
-	<div class="button-container">
+	<div class="flex flex-wrap gap-4 mb-6 items-center">
 		<button
-			class="btn btn-primary btn-sm"
-			data-toggle="modal"
-			data-target="#exampleModal"
+			class="bg-blue-500 hover:bg-blue-600 text-white text-sm font-medium px-4 py-2 rounded-lg shadow"
 			on:click={openModal}
 		>
 			Add
 		</button>
 
-		<select class="form-control order-select" on:change={updateCurrency} value={currency}>
+		<select
+			class="w-40 px-3 py-2 rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-400"
+			on:change={updateCurrency}
+			bind:value={currency}
+		>
 			<option value="BRL">BRL</option>
 			<option value="EUR">EUR</option>
-			<option value="USD" selected>USD</option>
+			<option value="USD">USD</option>
 		</select>
 
-		<select class="form-control order-select" on:change={updateOrderBy} value="{orderBy}">
+		<select
+			class="w-52 px-3 py-2 rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-400"
+			on:change={updateOrderBy}
+			bind:value={orderBy}
+		>
 			<option value="code">Order by Code</option>
 			<option value="name">Order by Name</option>
-			<option value="total" selected>Order by Total</option>
+			<option value="total">Order by Total</option>
 			<option value="weight">Order by Weight</option>
 		</select>
 	</div>
@@ -334,7 +340,6 @@
 				return;
 			}
 			searchStockResult = data;
-
 			const alreadyInPortfolio = result.some((s) => s.code === data[0]?.code);
 			if (data.length === 1 && !alreadyInPortfolio) {
 				await addSelectedStock(data[0]);
@@ -349,78 +354,68 @@
 	/>
 
 	{#if showDeleteConfirm}
-		<div class="modal-confirm">
-			<div class="modal-actions">
-				<button class="btn btn-danger" on:click={confirmDeleteAction}>Confirm deletion</button>
-				<button class="btn btn-cancel" on:click={cancelDelete}>Cancel</button>
+		<div class="fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-white dark:bg-gray-800 p-6 rounded-xl shadow-lg z-50 text-center">
+			<div class="flex justify-center gap-4">
+				<button class="bg-red-600 hover:bg-red-700 text-white px-4 py-2 rounded-lg" on:click={confirmDeleteAction}>Confirm deletion</button>
+				<button class="bg-gray-300 hover:bg-gray-400 text-gray-800 px-4 py-2 rounded-lg" on:click={cancelDelete}>Cancel</button>
 			</div>
 		</div>
 	{/if}
 
-	<div in:fade class="table-container">
-		<table class="stock-table">
-			<thead>
+	<div in:fade class="overflow-x-auto mt-6 rounded-xl shadow">
+		<table class="min-w-full bg-white dark:bg-gray-900 text-sm">
+			<thead class="bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-200">
 			<tr>
-				<th>Total Value</th>
-				<th>Total Assets</th>
+				<th class="px-4 py-3 text-left font-semibold">Total Value</th>
+				<th class="px-4 py-3 text-left font-semibold">Total Assets</th>
 			</tr>
 			</thead>
 			<tbody>
-			<tr>
-				<td class="code">{formatCurrency(totalValue)}</td>
-				<td class="code">{totalAssets}</td>
+			<tr class="border-t border-gray-200 dark:border-gray-700">
+				<td class="px-4 py-3 font-semibold text-gray-800 dark:text-gray-100">{formatCurrency(totalValue)}</td>
+				<td class="px-4 py-3 font-semibold text-gray-800 dark:text-gray-100">{totalAssets}</td>
 			</tr>
 			</tbody>
 		</table>
 	</div>
 
-	<div in:fade class="table-container">
-		<table class="stock-table">
-			<thead>
+	<div in:fade class="overflow-x-auto mt-6 rounded-xl shadow">
+		<table class="min-w-full bg-white dark:bg-gray-900 text-sm">
+			<thead class="bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-200">
 			<tr>
-				<th>Code</th>
-				<th>Name</th>
-				<th>Qty</th>
-				<th>Price</th>
-				<th>Total</th>
-				<th>% of Portfolio</th>
-				<th scope="col"></th>
+				<th class="px-4 py-3">Code</th>
+				<th class="px-4 py-3">Name</th>
+				<th class="px-4 py-3">Qty</th>
+				<th class="px-4 py-3">Price</th>
+				<th class="px-4 py-3">Total</th>
+				<th class="px-4 py-3">% of Portfolio</th>
+				<th class="px-4 py-3"></th>
 			</tr>
 			</thead>
 			<tbody>
 			{#each result as stock}
-				<tr>
-					<td class="code">
-						<div class="col-code" title="{stock.code}" on:click={openStock(stock.code)}>
-							<img
-								src={`https://flagcdn.com/w20/${getFlag(stock.code)}.png`}
-								alt="{getFlag(stock.code)} flag"
-								class="flag-icon"
-							/>
+				<tr class="odd:bg-gray-50 even:bg-gray-100 dark:odd:bg-gray-800 dark:even:bg-gray-700 hover:bg-blue-50 dark:hover:bg-blue-900 transition">
+					<td class="px-4 py-2 font-mono font-semibold text-blue-700 dark:text-blue-300 cursor-pointer" title={stock.code} on:click={() => openStock(stock.code)}>
+						<div class="inline-flex items-center gap-2">
+							<img src={`https://flagcdn.com/w20/${getFlag(stock.code)}.png`} alt="{getFlag(stock.code)} flag" class="w-5 h-auto" />
 							{formatCode(stock.code)}
 						</div>
 					</td>
-					<td class="name">{stock.name}</td>
-					<td class="qty-edit">
-						<form id="updateQuantity" on:submit|preventDefault={updateStockQuantity}>
+					<td class="px-4 py-2 text-gray-700 dark:text-gray-300">{stock.name}</td>
+					<td class="px-4 py-2">
+						<form id="updateQuantity" on:submit|preventDefault={updateStockQuantity} class="flex items-center">
 							<input type="hidden" name="code" value={stock.code} />
-							<input
-								type="number"
-								class="qty-input"
-								name="quantity"
-								min="0"
-								value={stock.quantity}
-								on:input={handleInputChange}
-							/>
+							<input type="number" name="quantity" min="0" value={stock.quantity}
+										 class="w-16 px-2 py-1 text-right border rounded-lg text-sm text-gray-800 dark:text-gray-100 bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-300" on:input={handleInputChange} />
 						</form>
 					</td>
-					<td class="price">{formatCurrency(stock.price)}</td>
-					<td class="total">{formatCurrency(stock.total)}</td>
-					<td class="percent">{calculatePercentage(stock.total, totalValue)}%</td>
-					<td>
-						<button class="remove-btn" aria-label="Delete" on:click={() => confirmDelete(stock.code)} title="remove">
-							<svg viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round"
-									 stroke-linejoin="round">
+					<td class="px-4 py-2 font-semibold text-green-600 dark:text-green-400">{formatCurrency(stock.price)}</td>
+					<td class="px-4 py-2 font-semibold text-gray-800 dark:text-gray-200">{formatCurrency(stock.total)}</td>
+					<td class="px-4 py-2 text-blue-600 dark:text-blue-400">{calculatePercentage(stock.total, totalValue)}%</td>
+					<td class="px-4 py-2 text-right">
+						<button on:click={() => confirmDelete(stock.code)} aria-label="Delete" title="remove"
+										class="bg-red-600 hover:bg-red-700 text-white rounded-full p-2 shadow focus:outline-none focus:ring-2 focus:ring-red-400">
+							<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4">
 								<polyline points="3 6 5 6 21 6"></polyline>
 								<path d="M19 6l-1 14H6L5 6"></path>
 								<path d="M10 11v6"></path>
@@ -436,300 +431,11 @@
 	</div>
 
 	{#if hasChanges}
-		<button class="btn btn-primary apply-changes-btn" on:click={applyChanges}>
+		<button class="fixed bottom-5 right-5 bg-blue-500 hover:bg-blue-600 text-white px-6 py-3 rounded-lg shadow-lg z-50" on:click={applyChanges}>
 			Apply Changes
 		</button>
 	{/if}
 {:else}
-	<div>No portfolio data available.</div>
+	<div class="text-gray-500 dark:text-gray-300 text-center py-6">No portfolio data available.</div>
 {/if}
 
-<style>
-    .apply-changes-btn {
-        position: fixed;
-        bottom: 20px;
-        right: 20px;
-        z-index: 100;
-    }
-
-    .table-container {
-        margin-top: 2rem;
-        overflow-x: auto;
-        border-radius: 12px;
-        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
-    }
-
-    .stock-table {
-        width: 100%;
-        border-collapse: collapse;
-        font-family: system-ui, sans-serif;
-        background-color: #fff;
-        border-radius: 12px;
-        overflow: hidden;
-        min-width: 600px;
-    }
-
-    th,
-    td {
-        padding: 0.5rem 1rem;
-        text-align: left;
-        white-space: nowrap;
-    }
-
-    thead {
-        background-color: #f7f7f7;
-        border-bottom: 2px solid #e0e0e0;
-    }
-
-    th {
-        font-weight: 600;
-        font-size: 0.95rem;
-        color: #333;
-    }
-
-    tbody tr:nth-child(odd) {
-        background-color: #fafafa;
-    }
-
-    tbody tr:nth-child(even) {
-        background-color: #f0f4f8;
-    }
-
-    tbody tr:hover {
-        background-color: #e1ecf4;
-    }
-
-    td {
-        font-size: 0.95rem;
-        color: #555;
-        border-bottom: 1px solid #eee;
-    }
-
-    .code {
-        font-weight: 600;
-        color: #2c3e50;
-    }
-
-    .col-code {
-        display: inline-block;
-        background-color: #e0f2ff;
-        color: #0369a1;
-        font-weight: 600;
-        font-family: monospace;
-        padding: 4px 10px;
-        border-radius: 9999px;
-        font-size: 0.875rem;
-        cursor: default;
-    }
-
-		.col-code:hover {
-        background-color: #035f8c;
-				color: #ffffff;
-		}
-
-    .name {
-        color: #7f8c8d;
-    }
-
-    .qty-edit {
-        display: flex;
-        align-items: center;
-        gap: 0.5rem;
-    }
-
-    .qty-input {
-        width: 60px;
-        padding: 0.4rem 0.5rem;
-        font-size: 0.9rem;
-        border: 1px solid #ccc;
-        border-radius: 6px;
-        text-align: right;
-        background-color: #fff;
-        color: #333;
-        transition: border-color 0.2s ease;
-    }
-
-    .qty-input:focus {
-        outline: none;
-        border-color: #2980b9;
-    }
-
-    .remove-btn {
-        height: 32px;
-        width: 32px;
-        background-color: #e74c3c;
-        border: none;
-        border-radius: 50%;
-        display: inline-flex;
-        align-items: center;
-        justify-content: center;
-        cursor: pointer;
-        transition: background-color 0.2s ease, transform 0.1s ease;
-        box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
-        padding: 0;
-    }
-
-    .remove-btn svg {
-        width: 16px;
-        height: 16px;
-    }
-
-    .remove-btn:hover {
-        background-color: #c0392b;
-        transform: scale(1.05);
-    }
-
-    .remove-btn:active {
-        transform: scale(0.95);
-    }
-
-    .remove-btn:focus {
-        outline: none;
-        box-shadow: 0 0 0 3px rgba(231, 76, 60, 0.4);
-    }
-
-    .btn-cancel {
-        background-color: #bdc3c7;
-        color: #2c3e50;
-        border: none;
-        border-radius: 8px;
-        padding: 0.6rem 1.2rem;
-        font-size: 1rem;
-        cursor: pointer;
-        transition: background-color 0.3s ease;
-    }
-
-    .btn-cancel:hover {
-        background-color: #aeb6bf;
-    }
-
-    .btn-cancel:focus {
-        outline: none;
-        box-shadow: 0 0 0 3px rgba(189, 195, 199, 0.5);
-    }
-
-    .modal-confirm {
-        position: fixed;
-        top: 50%;
-        left: 50%;
-        transform: translate(-50%, -50%);
-        background: white;
-        padding: 2rem;
-        border-radius: 12px;
-        box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
-        z-index: 1000;
-        text-align: center;
-        max-width: 90%;
-        width: 400px;
-        animation: fadeIn 0.2s ease;
-    }
-
-    .modal-actions {
-        display: flex;
-        justify-content: center;
-        gap: 1rem;
-    }
-
-    .btn-danger {
-        background-color: #e74c3c;
-        color: white;
-    }
-
-    .btn-danger:hover {
-        background-color: #c0392b;
-    }
-
-    .price {
-        color: #27ae60;
-        font-weight: bold;
-    }
-
-    .total {
-        font-weight: 500;
-        color: #34495e;
-    }
-
-    .percent {
-        color: #2980b9;
-    }
-
-    @media (max-width: 768px) {
-        input[type='number'] {
-            font-size: 0.9rem;
-        }
-    }
-
-    @media (max-width: 768px) {
-        .stock-table {
-            font-size: 0.875rem;
-        }
-
-        th,
-        td {
-            padding: 0.75rem 1rem;
-        }
-    }
-
-    .button-container {
-        display: flex;
-        gap: 10px;
-        margin-bottom: 1rem;
-        align-items: center;
-    }
-
-    .order-select {
-        width: 200px;
-        padding: 0.5rem;
-        border-radius: 8px;
-        border: 1px solid #ddd;
-        font-size: 1rem;
-        background-color: #f9f9f9;
-        transition: border-color 0.3s ease;
-    }
-
-    .order-select:focus {
-        border-color: #2980b9;
-        outline: none;
-    }
-
-    input[type='number'] {
-        font-size: 0.8rem;
-        border-radius: 8px;
-        border: 1px solid #ddd;
-        background-color: #f9f9f9;
-        color: #333;
-        transition: border-color 0.3s ease;
-    }
-
-    input[type='number']:focus {
-        border-color: #2980b9;
-        outline: none;
-    }
-
-    .btn {
-        padding: 0.6rem 1.2rem;
-        font-size: 1rem;
-        border-radius: 8px;
-        border: none;
-        cursor: pointer;
-        transition: background-color 0.3s ease;
-    }
-
-    .btn-primary {
-        background-color: #3498db;
-        color: white;
-    }
-
-    .btn-primary:hover {
-        background-color: #2980b9;
-    }
-
-    @keyframes fadeIn {
-        from {
-            opacity: 0;
-        }
-        to {
-            opacity: 1;
-        }
-    }
-</style>

+ 66 - 35
src/routes/profile/+page.svelte

@@ -80,55 +80,86 @@
 	<meta name="description" content="Profile" />
 </svelte:head>
 
-<div in:fade class="profile-container">
+<div in:fade class="max-w-xl mx-auto mt-8 px-4">
 	{#if isLoading}
-		<p>Loading...</p>
+		<p class="text-center text-gray-500 dark:text-gray-400">Loading...</p>
 	{:else if isAuthenticated}
-		<div class="profile-header">
-			<!--			<p class="welcome-message">Logged in as <strong>{user.username}</strong></p>-->
+		<div class="mb-6 text-center">
+			<!-- Optional welcome message -->
+			<!-- <p class="text-lg text-gray-700 dark:text-gray-300">Logged in as <strong>{user.username}</strong></p> -->
 		</div>
 
-		<div class="settings-card">
-			<h2>Preferences</h2>
-			<div class="settings-group">
-				<label for="currency">Currency:</label>
-				<select id="currency" class="form-control" on:change={updateCurrency} bind:value={currency}>
-					<option value="BRL">BRL</option>
-					<option value="EUR">EUR</option>
-					<option value="USD">USD</option>
-				</select>
+		<div class="bg-white dark:bg-gray-900 rounded-xl shadow p-6 space-y-6">
+			<h2 class="text-xl font-semibold text-gray-800 dark:text-gray-100">Preferences</h2>
+
+			<div class="space-y-4">
+				<div class="flex flex-col gap-1">
+					<label for="currency" class="text-sm font-medium text-gray-600 dark:text-gray-300">Currency</label>
+					<select
+						id="currency"
+						class="w-full px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
+						on:change={updateCurrency}
+						bind:value={currency}
+					>
+						<option value="BRL">BRL</option>
+						<option value="EUR">EUR</option>
+						<option value="USD">USD</option>
+					</select>
+				</div>
+
+				<div class="flex flex-col gap-1">
+					<label for="orderBy" class="text-sm font-medium text-gray-600 dark:text-gray-300">Order By</label>
+					<select
+						id="orderBy"
+						class="w-full px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
+						on:change={updateOrderBy}
+						bind:value={orderBy}
+					>
+						<option value="code">Code</option>
+						<option value="name">Name</option>
+						<option value="total">Total</option>
+						<option value="weight">Weight</option>
+					</select>
+				</div>
+
+				<div class="flex flex-col gap-1">
+					<label for="theme" class="text-sm font-medium text-gray-600 dark:text-gray-300">Theme</label>
+					<select
+						id="theme"
+						class="w-full px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
+					>
+						<option value="light">Light</option>
+						<!-- <option value="dark">Dark</option> -->
+					</select>
+				</div>
 			</div>
 
-			<div class="settings-group">
-				<label for="orderBy">Order By:</label>
-				<select id="orderBy" class="form-control" on:change={updateOrderBy} bind:value={orderBy}>
-					<option value="code">Code</option>
-					<option value="name">Name</option>
-					<option value="total">Total</option>
-					<option value="weight">Weight</option>
-				</select>
+			<div class="flex flex-col sm:flex-row sm:justify-between gap-3 pt-4">
+				<button
+					class="bg-blue-500 hover:bg-blue-600 text-white px-5 py-2 rounded-lg transition"
+					on:click={savePreferences}
+					disabled={savePreferenceDisabled}
+				>
+					Save preferences
+				</button>
+				<button
+					class="bg-red-500 hover:bg-red-600 text-white px-5 py-2 rounded-lg transition"
+					on:click={logout}
+				>
+					Logout
+				</button>
 			</div>
-
-			<div class="settings-group">
-				<label for="theme">Theme:</label>
-				<select id="theme" class="form-control">
-					<option value="light">Light</option>
-					<!-- <option value="dark">Dark</option> -->
-				</select>
-			</div>
-
-			<button class="save-preferences" on:click={savePreferences} disabled={savePreferenceDisabled}>Save preferences
-			</button>
-			<button class="logout-button" on:click={logout}>Logout</button>
 		</div>
 
 		{#if saveMessage}
-			<div in:fade out:fade class="save-message">{saveMessage}</div>
+			<div in:fade out:fade class="mt-4 text-center text-green-600 dark:text-green-400 font-medium">
+				{saveMessage}
+			</div>
 		{/if}
-
 	{/if}
 </div>
 
+
 <style>
     .profile-container {
         max-width: 600px;

+ 42 - 59
src/routes/register/+page.svelte

@@ -67,19 +67,29 @@
 	<meta name="description" content="Registration page" />
 </svelte:head>
 
-<div in:fade class="text-column">
-	<div class="card">
-		<form on:submit={submit}>
-			<input type="text" name="username" placeholder="username" class="input-field" />
-
-			<div class="password-container">
+<div in:fade class="flex justify-center items-center min-h-[70vh] px-4">
+	<div class="w-full max-w-sm bg-white dark:bg-gray-900 shadow-md rounded-xl p-6 space-y-6">
+		<form on:submit={submit} class="space-y-4">
+			<!-- Username -->
+			<input
+				type="text"
+				name="username"
+				placeholder="Username"
+				class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
+			/>
+
+			<!-- Password -->
+			<div class="relative">
 				<input
 					type={showPassword ? 'text' : 'password'}
 					name="password"
-					placeholder="password"
-					class="input-field"
+					placeholder="Password"
+					class="w-full px-4 py-2 pr-10 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
 				/>
-				<span class="eye-icon" on:click={togglePasswordVisibility}>
+				<span
+					class="absolute inset-y-0 right-3 flex items-center cursor-pointer text-gray-500 dark:text-gray-400 text-lg"
+					on:click={togglePasswordVisibility}
+				>
 					{#if showPassword}
 						👁️‍🗨️
 					{:else}
@@ -88,14 +98,18 @@
 				</span>
 			</div>
 
-			<div class="password-container">
+			<!-- Repeat Password -->
+			<div class="relative">
 				<input
 					type={showPassword ? 'text' : 'password'}
 					name="passwordConfirm"
-					placeholder="repeat password"
-					class="input-field"
+					placeholder="Repeat Password"
+					class="w-full px-4 py-2 pr-10 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
 				/>
-				<span class="eye-icon" on:click={togglePasswordVisibility}>
+				<span
+					class="absolute inset-y-0 right-3 flex items-center cursor-pointer text-gray-500 dark:text-gray-400 text-lg"
+					on:click={togglePasswordVisibility}
+				>
 					{#if showPassword}
 						👁️‍🗨️
 					{:else}
@@ -104,52 +118,21 @@
 				</span>
 			</div>
 
-			<button type="submit" class="register-button">Register</button>
-			<button class="cancel-button" on:click={cancel}>Cancel</button>
+			<!-- Buttons -->
+			<button
+				type="submit"
+				class="w-full bg-blue-500 hover:bg-blue-600 text-white py-2 rounded-lg transition"
+			>
+				Register
+			</button>
+
+			<button
+				type="button"
+				class="w-full bg-gray-300 hover:bg-gray-400 text-gray-800 py-2 rounded-lg transition"
+				on:click={cancel}
+			>
+				Cancel
+			</button>
 		</form>
 	</div>
 </div>
-
-<style>
-    input::placeholder {
-        opacity: 0.5;
-    }
-
-    .password-container {
-        position: relative;
-    }
-
-    .eye-icon {
-        position: absolute;
-        right: 10px;
-        top: 50%;
-        transform: translateY(-50%);
-        cursor: pointer;
-        font-size: 1.1rem;
-        color: #666;
-        user-select: none;
-    }
-
-    .input-field {
-        width: 100%;
-        padding: 0.5rem 2.5rem 0.5rem 0.5rem;
-        margin-bottom: 1rem;
-        box-sizing: border-box;
-    }
-
-    .register-button,
-    .cancel-button {
-        padding: 0.5rem 1rem;
-        margin-top: 0.5rem;
-        border: none;
-        border-radius: 4px;
-        cursor: pointer;
-        font-weight: bold;
-    }
-
-    .cancel-button {
-        background-color: #f44336;
-        color: white;
-    }
-
-</style>

+ 27 - 138
src/routes/stocks/+page.svelte

@@ -77,39 +77,52 @@
 </svelte:head>
 
 {#if isLoading}
-	<div in:fade>Loading...</div>
+	<div in:fade class="text-center text-gray-600 dark:text-gray-300 py-8">Loading...</div>
 {:else}
-	<div class="filter-card">
+	<!-- Filter Input -->
+	<div class="mx-auto my-4 w-11/12 max-w-3xl px-4 py-4 bg-gradient-to-r from-gray-50 to-white dark:from-gray-800 dark:to-gray-900 shadow-md rounded-xl">
 		<input
 			type="text"
 			bind:value={stockQuery}
 			placeholder="Filter by name or code"
-			class="input-field"
+			class="w-full px-4 py-3 text-base border border-gray-300 dark:border-gray-600 rounded-lg outline-none focus:ring-2 focus:ring-blue-300 dark:focus:ring-blue-600 bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-100"
 			autofocus
 		/>
 	</div>
 
+	<!-- Results -->
 	{#if filteredResults.length !== 0}
-		<div in:fade class="result">
+		<div in:fade class="grid gap-6 p-6 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4">
 			{#each filteredResults as stock}
-				<div class="card">
-					<div class="stock-info">
-						<div class="stock-code">
+				<div
+					class="bg-white dark:bg-gray-800 border border-transparent dark:border-gray-700 hover:shadow-xl transition-all rounded-xl p-5 flex flex-col justify-between hover:-translate-y-1 duration-200"
+				>
+					<!-- Stock Info -->
+					<div class="flex justify-between items-start border-b border-gray-200 dark:border-gray-700 pb-3">
+						<div class="flex items-center gap-2 text-lg font-bold text-gray-900 dark:text-gray-100">
 							<img
 								src={`https://flagcdn.com/16x12/${getFlag(stock.code)}.png`}
 								alt="{getFlag(stock.code)} flag"
-								class="flag-icon"
+								class="w-4 h-auto"
 							/>
 							{stock.code}
 						</div>
-						<div class="stock-name">{stock.name}</div>
+						<div class="text-sm text-right text-gray-500 dark:text-gray-400 max-w-[60%] leading-snug">
+							{stock.name}
+						</div>
 					</div>
-					<div class="stock-details">
-						<div class="stock-currency">{stock.currency}</div>
-						<div class="stock-price">{formatCurrency(stock.price, stock.currency)}</div>
+
+					<!-- Stock Details -->
+					<div class="flex justify-between items-center mt-4">
+						<div class="text-sm text-gray-500 dark:text-gray-400">{stock.currency}</div>
+						<div class="text-xl font-bold text-green-600 dark:text-green-400">
+							{formatCurrency(stock.price, stock.currency)}
+						</div>
 					</div>
+
+					<!-- View Button -->
 					<a
-						class="view-button"
+						class="mt-4 self-end px-4 py-2 bg-blue-500 hover:bg-blue-600 text-white text-sm font-medium rounded-md transition-colors duration-200"
 						target="_blank"
 						rel="noopener noreferrer"
 						on:click={openStock(stock.code)}
@@ -120,130 +133,6 @@
 			{/each}
 		</div>
 	{:else}
-		<p>No matching results.</p>
+		<p class="text-center text-gray-400 text-base mt-8">No matching results.</p>
 	{/if}
 {/if}
-
-<style>
-    .filter-card {
-        margin: 0.5rem auto;
-        padding: 1rem 1.5rem;
-        background: linear-gradient(to right, #f9f9f9, #ffffff);
-        border-radius: 1rem;
-        box-shadow: 0 4px 10px rgba(0, 0, 0, 0.05);
-        width: 90%;
-        max-width: none;
-        display: flex;
-        justify-content: center;
-    }
-
-    .input-field {
-        width: 100%;
-        max-width: 100%;
-        padding: 0.75rem 1rem;
-        font-size: 1rem;
-        border: 1px solid #ccc;
-        border-radius: 0.75rem;
-        outline: none;
-        transition: border-color 0.2s, box-shadow 0.2s;
-        background-color: #fff;
-    }
-
-    .input-field:focus {
-        border-color: #4a90e2;
-        box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.2);
-    }
-
-    .result {
-        display: grid;
-        grid-template-columns: repeat(auto-fit, minmax(275px, 1fr));
-        gap: 1.5rem;
-        padding: 2rem;
-    }
-
-    .card {
-				max-width: 275px;
-        background: #ffffff;
-        border-radius: 1rem;
-        box-shadow: 0 6px 14px rgba(0, 0, 0, 0.05);
-        padding: 1.25rem;
-        transition: transform 0.3s ease, box-shadow 0.3s ease;
-        display: flex;
-        flex-direction: column;
-        justify-content: space-between;
-        border: 1px solid transparent;
-    }
-
-    .card:hover {
-        transform: translateY(-6px);
-        box-shadow: 0 10px 24px rgba(0, 0, 0, 0.08);
-        border-color: #eee;
-    }
-
-    .stock-info {
-        display: flex;
-        justify-content: space-between;
-        align-items: flex-start;
-        border-bottom: 1px solid #f0f0f0;
-        padding-bottom: 0.75rem;
-    }
-
-    .stock-code {
-        font-size: 1.4rem;
-        font-weight: 700;
-        color: #2c3e50;
-    }
-
-    .stock-name {
-        font-size: 1rem;
-        color: #7f8c8d;
-        text-align: right;
-        max-width: 60%;
-        line-height: 1.2;
-    }
-
-    .stock-details {
-        display: flex;
-        justify-content: space-between;
-        align-items: center;
-        margin-top: 0.75rem;
-    }
-
-    .stock-currency {
-        font-size: 0.9rem;
-        color: #999;
-    }
-
-    .stock-price {
-        font-size: 1.3rem;
-        font-weight: bold;
-        color: #2ecc71;
-    }
-
-    .view-button {
-        margin-top: 1rem;
-        align-self: flex-end;
-        padding: 0.5rem 1rem;
-        background-color: #3498db;
-        color: white;
-        font-size: 0.9rem;
-        border: none;
-        border-radius: 0.5rem;
-        cursor: pointer;
-        transition: background-color 0.2s ease;
-        text-decoration: none;
-        text-align: center;
-    }
-
-    .view-button:hover {
-        background-color: #2980b9;
-    }
-
-    p {
-        text-align: center;
-        color: #aaa;
-        font-size: 1rem;
-        margin-top: 2rem;
-    }
-
-</style>

+ 80 - 149
src/routes/stocks/[code]/+page.svelte

@@ -62,182 +62,113 @@
 </svelte:head>
 
 {#if !stockInfo}
-	<div class="loading">Loading...</div>
+	<div class="text-center py-8 text-lg text-gray-500 dark:text-gray-400">Loading...</div>
 {:else}
-	<div class="stock-page">
-		<div class="stock-header">
+	<div class="w-full bg-white dark:bg-gray-900 rounded-xl shadow-md p-6 box-border transition-all duration-300 max-w-6xl mx-auto mt-6">
+		<!-- Header -->
+		<div class="flex items-center gap-4 border-b border-gray-200 dark:border-gray-700 pb-6 mb-8">
 			<img
 				src={`https://flagcdn.com/w20/${getFlag(stockInfo.code)}.png`}
 				alt="{getFlag(code)} flag"
-				class="flag-icon"
+				class="w-6 h-4 object-contain"
 			/>
-			<h1>{stockInfo.code}</h1>
-			<h2>{stockInfo.name}</h2>
+			<h1 class="text-3xl font-bold text-gray-800 dark:text-gray-100 m-0">{stockInfo.code}</h1>
+			<h2 class="text-lg font-normal text-gray-500 dark:text-gray-400 m-0">{stockInfo.name}</h2>
 		</div>
 
-		<div class="stock-body">
-			<div class="stock-line">
-				<span class="label">Code:</span>
-				<span class="value">{stockInfo.code}</span>
+		<!-- Details Grid -->
+		<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
+			<div class="bg-gray-50 dark:bg-gray-800 p-5 rounded-lg shadow-sm flex justify-between items-center">
+				<span class="text-gray-600 dark:text-gray-300 font-medium text-base">Code:</span>
+				<span class="text-emerald-600 dark:text-emerald-400 font-semibold text-lg">{stockInfo.code}</span>
 			</div>
 
-			<div class="stock-line">
-				<span class="label">Name:</span>
-				<span class="value">{stockInfo.name}</span>
+			<div class="bg-gray-50 dark:bg-gray-800 p-5 rounded-lg shadow-sm flex justify-between items-center">
+				<span class="text-gray-600 dark:text-gray-300 font-medium text-base">Name:</span>
+				<span class="text-emerald-600 dark:text-emerald-400 font-semibold text-lg">{stockInfo.name}</span>
 			</div>
 
-			<div class="stock-line">
-				<span class="label">Description:</span>
-				<span class="value">{stockInfo.description ?? 'N/A'}</span>
+			<div class="bg-gray-50 dark:bg-gray-800 p-5 rounded-lg shadow-sm flex justify-between items-center">
+				<span class="text-gray-600 dark:text-gray-300 font-medium text-base">Description:</span>
+				<span class="text-emerald-600 dark:text-emerald-400 font-semibold text-lg">
+					{stockInfo.description ?? 'N/A'}
+				</span>
 			</div>
 
-			<div class="stock-line">
-				<span class="label">Foundation:</span>
-				<span class="value">{stockInfo.foundation ?? 'N/A'}</span>
+			<div class="bg-gray-50 dark:bg-gray-800 p-5 rounded-lg shadow-sm flex justify-between items-center">
+				<span class="text-gray-600 dark:text-gray-300 font-medium text-base">Foundation:</span>
+				<span class="text-emerald-600 dark:text-emerald-400 font-semibold text-lg">
+					{stockInfo.foundation ?? 'N/A'}
+				</span>
 			</div>
 
-			<div class="stock-line">
-				<span class="label">IPO:</span>
-				<span class="value">{stockInfo.ipo ?? 'N/A'}</span>
+			<div class="bg-gray-50 dark:bg-gray-800 p-5 rounded-lg shadow-sm flex justify-between items-center">
+				<span class="text-gray-600 dark:text-gray-300 font-medium text-base">IPO:</span>
+				<span class="text-emerald-600 dark:text-emerald-400 font-semibold text-lg">{stockInfo.ipo ?? 'N/A'}</span>
 			</div>
 
-			<div class="stock-line">
-				<span class="label">Exchange:</span>
-				<span class="value">{stockInfo.exchange ?? 'N/A'}</span>
+			<div class="bg-gray-50 dark:bg-gray-800 p-5 rounded-lg shadow-sm flex justify-between items-center">
+				<span class="text-gray-600 dark:text-gray-300 font-medium text-base">Exchange:</span>
+				<span class="text-emerald-600 dark:text-emerald-400 font-semibold text-lg">
+					{stockInfo.exchange ?? 'N/A'}
+				</span>
 			</div>
 
-			<div class="stock-line">
-				<span class="label">Headquarters:</span>
-				<span class="value">{stockInfo.headquarters ?? 'N/A'}</span>
+			<div class="bg-gray-50 dark:bg-gray-800 p-5 rounded-lg shadow-sm flex justify-between items-center">
+				<span class="text-gray-600 dark:text-gray-300 font-medium text-base">Headquarters:</span>
+				<span class="text-emerald-600 dark:text-emerald-400 font-semibold text-lg">
+					{stockInfo.headquarters ?? 'N/A'}
+				</span>
 			</div>
 
-			<div class="stock-line">
-				<span class="label">Industry:</span>
-				<span class="value">{stockInfo.industry ?? 'N/A'}</span>
+			<div class="bg-gray-50 dark:bg-gray-800 p-5 rounded-lg shadow-sm flex justify-between items-center">
+				<span class="text-gray-600 dark:text-gray-300 font-medium text-base">Industry:</span>
+				<span class="text-emerald-600 dark:text-emerald-400 font-semibold text-lg">
+					{stockInfo.industry ?? 'N/A'}
+				</span>
 			</div>
 
-			<div class="stock-line">
-				<span class="label">Company Website:</span>
-				<span class="value">
-			{#if stockInfo.companyWebsite}
-				<a href={stockInfo.companyWebsite} target="_blank" rel="noopener noreferrer">website</a>
-			{:else}
-				N/A
-			{/if}
-		</span>
+			<div class="bg-gray-50 dark:bg-gray-800 p-5 rounded-lg shadow-sm flex justify-between items-center">
+				<span class="text-gray-600 dark:text-gray-300 font-medium text-base">Company Website:</span>
+				<span class="text-emerald-600 dark:text-emerald-400 font-semibold text-lg">
+					{#if stockInfo.companyWebsite}
+						<a
+							href={stockInfo.companyWebsite}
+							target="_blank"
+							rel="noopener noreferrer"
+							class="underline text-blue-600 dark:text-blue-400"
+						>website</a>
+					{:else}
+						N/A
+					{/if}
+				</span>
 			</div>
 
-			<div class="stock-line">
-				<span class="label">SEC Website:</span>
-				<span class="value">
-			{#if stockInfo.secWebsite}
-				<a href={stockInfo.secWebsite} target="_blank" rel="noopener noreferrer">sec</a>
-			{:else}
-				N/A
-			{/if}
-		</span>
+			<div class="bg-gray-50 dark:bg-gray-800 p-5 rounded-lg shadow-sm flex justify-between items-center">
+				<span class="text-gray-600 dark:text-gray-300 font-medium text-base">SEC Website:</span>
+				<span class="text-emerald-600 dark:text-emerald-400 font-semibold text-lg">
+					{#if stockInfo.secWebsite}
+						<a
+							href={stockInfo.secWebsite}
+							target="_blank"
+							rel="noopener noreferrer"
+							class="underline text-blue-600 dark:text-blue-400"
+						>sec</a>
+					{:else}
+						N/A
+					{/if}
+				</span>
 			</div>
-
-			<a
-				class="view-button"
-				href="https://www.google.com/search?q={stockInfo.code}+{stockInfo.name}+stock"
-				target="_blank"
-				rel="noopener noreferrer"
-			>
-				View More
-			</a>
-
 		</div>
 
+		<!-- External link -->
+		<a
+			class="inline-block mt-6 bg-blue-500 hover:bg-blue-600 text-white font-medium px-4 py-2 rounded-md transition"
+			href="https://www.google.com/search?q={stockInfo.code}+{stockInfo.name}+stock"
+			target="_blank"
+			rel="noopener noreferrer"
+		>
+			View More
+		</a>
 	</div>
 {/if}
-
-<style>
-    .loading {
-        text-align: center;
-        padding: 2rem;
-        font-size: 1.2rem;
-        color: #888;
-    }
-
-    .stock-page {
-        width: 100%;
-        background: #ffffff;
-        box-sizing: border-box;
-        border-radius: 1rem;
-        box-shadow: 0 6px 14px rgba(0, 0, 0, 0.05);
-        padding: 1.25rem;
-        transition: transform 0.3s ease, box-shadow 0.3s ease;
-    }
-
-    .stock-header {
-        display: flex;
-        align-items: center;
-        gap: 1rem;
-        border-bottom: 2px solid #f0f0f0;
-        padding-bottom: 1.5rem;
-        margin-bottom: 2rem;
-    }
-
-    .flag-icon {
-        width: auto;
-        height: auto;
-    }
-
-    h1 {
-        font-size: 2.5rem;
-        color: #2c3e50;
-        margin: 0;
-    }
-
-    h2 {
-        font-size: 1.25rem;
-        color: #7f8c8d;
-        font-weight: normal;
-        margin: 0;
-    }
-
-    .stock-body {
-        display: grid;
-        grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
-        gap: 2rem;
-    }
-
-    .stock-line {
-        background: #f9f9f9;
-        padding: 1.25rem;
-        border-radius: 0.75rem;
-        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
-        display: flex;
-        justify-content: space-between;
-        align-items: center;
-    }
-
-    .label {
-        color: #666;
-        font-weight: 500;
-        font-size: 1.1rem;
-    }
-
-    .value {
-        color: #2ecc71;
-        font-weight: 700;
-        font-size: 1.2rem;
-    }
-
-    .view-button {
-        margin-top: 1rem;
-        align-self: flex-end;
-        padding: 0.5rem 1rem;
-        background-color: #3498db;
-        color: white;
-        font-size: 0.9rem;
-        border: none;
-        border-radius: 0.5rem;
-        cursor: pointer;
-        transition: background-color 0.2s ease;
-        text-decoration: none;
-        text-align: center;
-				width: 75px;
-    }
-</style>

+ 0 - 0
static/css/main.css


+ 19 - 0
tailwind.config.js

@@ -0,0 +1,19 @@
+export default {
+	darkMode: 'class',
+	content: ['./src/**/*.{html,js,svelte,ts}'],
+	theme: {
+		extend: {
+			fontFamily: {
+				body: ['Roboto', 'Arial', 'AppleGothic', 'serif'],
+				mono: ['Fira Mono', 'monospace']
+			},
+			colors: {
+				theme: {
+					light: '#4075a6',
+					dark: '#2c3e50'
+				}
+			}
+		}
+	},
+	plugins: []
+};