chore: Update Tailwind CSS and dependencies
- Downgraded Tailwind CSS to version 3.4.17 for compatibility. - Updated various dependencies to resolve version conflicts and ensure smooth operation. - Added missing types for Node.js to enhance type safety. - Improved the theming system using CSS custom properties (variables) for better customization and dark mode support. - Refactored styles in `app.css` to improve maintainability and readability. Updated the color palette to enhance the user experience. - Updated the PostCSS configuration to use the new Tailwind CSS version. - Updated component styles to utilize the new theming system.
This commit is contained in:
parent
da0ced9b4a
commit
f22e9faae2
@ -21,7 +21,7 @@
|
|||||||
"postcss": "^8.5.3",
|
"postcss": "^8.5.3",
|
||||||
"svelte": "^5.28.1",
|
"svelte": "^5.28.1",
|
||||||
"svelte-check": "^4.1.6",
|
"svelte-check": "^4.1.6",
|
||||||
"tailwindcss": "^4.1.6",
|
"tailwindcss": "^3.4.17",
|
||||||
"typescript": "~5.8.3",
|
"typescript": "~5.8.3",
|
||||||
"vite": "^6.3.5"
|
"vite": "^6.3.5"
|
||||||
},
|
},
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
|||||||
export default {
|
export default {
|
||||||
plugins: {
|
plugins: {
|
||||||
'@tailwindcss/postcss': {},
|
tailwindcss: {},
|
||||||
autoprefixer: {},
|
autoprefixer: {},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
108
sweb/src/app.css
108
sweb/src/app.css
@ -1,11 +1,115 @@
|
|||||||
@import "tailwindcss";
|
|
||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
|
:root {
|
||||||
|
/* Primary color (blue) */
|
||||||
|
--color-primary-50: 239 246 255;
|
||||||
|
/* blue-50 */
|
||||||
|
--color-primary-100: 219 234 254;
|
||||||
|
/* blue-100 */
|
||||||
|
--color-primary-200: 191 219 254;
|
||||||
|
/* blue-200 */
|
||||||
|
--color-primary-300: 147 197 253;
|
||||||
|
/* blue-300 */
|
||||||
|
--color-primary-400: 96 165 250;
|
||||||
|
/* blue-400 */
|
||||||
|
--color-primary-500: 59 130 246;
|
||||||
|
/* blue-500 */
|
||||||
|
--color-primary-600: 37 99 235;
|
||||||
|
/* blue-600 */
|
||||||
|
--color-primary-700: 29 78 216;
|
||||||
|
/* blue-700 */
|
||||||
|
--color-primary-800: 30 64 175;
|
||||||
|
/* blue-800 */
|
||||||
|
--color-primary-900: 30 58 138;
|
||||||
|
/* blue-900 */
|
||||||
|
|
||||||
|
/* Background colors */
|
||||||
|
--color-background: 249 250 251;
|
||||||
|
/* gray-50 */
|
||||||
|
--color-background-secondary: 255 255 255;
|
||||||
|
/* white */
|
||||||
|
|
||||||
|
/* Text colors */
|
||||||
|
--color-text: 17 24 39;
|
||||||
|
/* gray-900 */
|
||||||
|
--color-text-secondary: 55 65 81;
|
||||||
|
/* gray-700 */
|
||||||
|
--color-text-muted: 107 114 128;
|
||||||
|
/* gray-500 */
|
||||||
|
|
||||||
|
/* Border colors */
|
||||||
|
--color-border: 229 231 235;
|
||||||
|
/* gray-200 */
|
||||||
|
--color-border-secondary: 209 213 219;
|
||||||
|
/* gray-300 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
/* Primary color (blue) - slightly adjusted for dark mode */
|
||||||
|
--color-primary-50: 30 58 138;
|
||||||
|
/* blue-900 */
|
||||||
|
--color-primary-100: 30 64 175;
|
||||||
|
/* blue-800 */
|
||||||
|
--color-primary-200: 29 78 216;
|
||||||
|
/* blue-700 */
|
||||||
|
--color-primary-300: 37 99 235;
|
||||||
|
/* blue-600 */
|
||||||
|
--color-primary-400: 59 130 246;
|
||||||
|
/* blue-500 */
|
||||||
|
--color-primary-500: 96 165 250;
|
||||||
|
/* blue-400 */
|
||||||
|
--color-primary-600: 147 197 253;
|
||||||
|
/* blue-300 */
|
||||||
|
--color-primary-700: 191 219 254;
|
||||||
|
/* blue-200 */
|
||||||
|
--color-primary-800: 219 234 254;
|
||||||
|
/* blue-100 */
|
||||||
|
--color-primary-900: 239 246 255;
|
||||||
|
/* blue-50 */
|
||||||
|
|
||||||
|
/* Background colors */
|
||||||
|
--color-background: 17 24 39;
|
||||||
|
/* gray-900 */
|
||||||
|
--color-background-secondary: 31 41 55;
|
||||||
|
/* gray-800 */
|
||||||
|
|
||||||
|
/* Text colors */
|
||||||
|
--color-text: 249 250 251;
|
||||||
|
/* gray-50 */
|
||||||
|
--color-text-secondary: 229 231 235;
|
||||||
|
/* gray-200 */
|
||||||
|
--color-text-muted: 156 163 175;
|
||||||
|
/* gray-400 */
|
||||||
|
|
||||||
|
/* Border colors */
|
||||||
|
--color-border: 55 65 81;
|
||||||
|
/* gray-700 */
|
||||||
|
--color-border-secondary: 75 85 99;
|
||||||
|
/* gray-600 */
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||||
|
@apply bg-background text-text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add utility classes for components */
|
||||||
|
.btn {
|
||||||
|
@apply inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
@apply bg-primary-600 text-white hover:bg-primary-700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
@apply bg-background-secondary text-text border border-border hover:bg-background hover:text-text-secondary;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-ghost {
|
||||||
|
@apply hover:bg-background-secondary hover:text-text-secondary;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,7 +3,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<footer
|
<footer
|
||||||
class="bg-gray-100 border-t border-gray-200 py-4 sm:py-6 px-3 sm:px-4 w-full mt-auto"
|
class="bg-background-secondary border-t border-border py-4 sm:py-6 px-3 sm:px-4 w-full mt-auto"
|
||||||
>
|
>
|
||||||
<div class="container mx-auto max-w-6xl">
|
<div class="container mx-auto max-w-6xl">
|
||||||
<div
|
<div
|
||||||
@ -11,8 +11,10 @@
|
|||||||
>
|
>
|
||||||
<!-- Logo and tagline -->
|
<!-- Logo and tagline -->
|
||||||
<div class="text-center sm:text-left">
|
<div class="text-center sm:text-left">
|
||||||
<div class="text-lg font-semibold text-blue-800">SecureWeb</div>
|
<div class="text-lg font-semibold text-primary-600">
|
||||||
<p class="text-gray-600 text-sm mt-1">
|
SecureWeb
|
||||||
|
</div>
|
||||||
|
<p class="text-text-secondary text-sm mt-1">
|
||||||
Secure and reliable web solutions
|
Secure and reliable web solutions
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -23,22 +25,22 @@
|
|||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href="/"
|
href="/"
|
||||||
class="text-gray-600 hover:text-blue-800 transition-colors text-sm sm:text-base"
|
class="text-text-secondary hover:text-primary-600 transition-colors text-sm sm:text-base"
|
||||||
>Home</a
|
>Home</a
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href="/about"
|
href="/about"
|
||||||
class="text-gray-600 hover:text-blue-800 transition-colors text-sm sm:text-base"
|
class="text-text-secondary hover:text-primary-600 transition-colors text-sm sm:text-base"
|
||||||
>About</a
|
>About</a
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href="/services"
|
href="/services"
|
||||||
class="text-gray-600 hover:text-blue-800 transition-colors text-sm sm:text-base"
|
class="text-text-secondary hover:text-primary-600 transition-colors text-sm sm:text-base"
|
||||||
>Services</a
|
>Services</a
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href="/contact"
|
href="/contact"
|
||||||
class="text-gray-600 hover:text-blue-800 transition-colors text-sm sm:text-base"
|
class="text-text-secondary hover:text-primary-600 transition-colors text-sm sm:text-base"
|
||||||
>Contact</a
|
>Contact</a
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
@ -48,7 +50,7 @@
|
|||||||
<a
|
<a
|
||||||
href="/twitter"
|
href="/twitter"
|
||||||
aria-label="Twitter"
|
aria-label="Twitter"
|
||||||
class="text-gray-500 hover:text-blue-800"
|
class="text-text-muted hover:text-primary-600"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
@ -64,7 +66,7 @@
|
|||||||
<a
|
<a
|
||||||
href="/github"
|
href="/github"
|
||||||
aria-label="GitHub"
|
aria-label="GitHub"
|
||||||
class="text-gray-500 hover:text-blue-800"
|
class="text-text-muted hover:text-primary-600"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
@ -80,7 +82,7 @@
|
|||||||
<a
|
<a
|
||||||
href="/linkedin"
|
href="/linkedin"
|
||||||
aria-label="LinkedIn"
|
aria-label="LinkedIn"
|
||||||
class="text-gray-500 hover:text-blue-800"
|
class="text-text-muted hover:text-primary-600"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
@ -97,7 +99,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="border-t border-gray-200 mt-4 sm:mt-6 pt-4 sm:pt-6 text-center text-gray-500 text-xs sm:text-sm"
|
class="border-t border-border mt-4 sm:mt-6 pt-4 sm:pt-6 text-center text-text-muted text-xs sm:text-sm"
|
||||||
>
|
>
|
||||||
<p>
|
<p>
|
||||||
© {new Date().getFullYear()} SecureWeb. All rights reserved.
|
© {new Date().getFullYear()} SecureWeb. All rights reserved.
|
||||||
|
@ -8,10 +8,10 @@
|
|||||||
$: actualPath = contentPath || "introduction/introduction";
|
$: actualPath = contentPath || "introduction/introduction";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="py-4 sm:py-6 md:py-8 px-3 sm:px-4 max-w-7xl mx-auto">
|
<div class="">
|
||||||
{#if contentPath}
|
{#if contentPath}
|
||||||
<div
|
<div
|
||||||
class="bg-white rounded-lg shadow-sm p-4 sm:p-6 mb-6 sm:mb-8 overflow-x-auto"
|
class="bg-background-secondary shadow-sm p-4 sm:p-6 overflow-x-auto"
|
||||||
>
|
>
|
||||||
<MarkdownContent path={actualPath} />
|
<MarkdownContent path={actualPath} />
|
||||||
</div>
|
</div>
|
||||||
@ -21,12 +21,14 @@
|
|||||||
<div class="md:flex md:items-center md:justify-between">
|
<div class="md:flex md:items-center md:justify-between">
|
||||||
<div class="md:w-1/2 mb-8 md:mb-0 md:pr-6">
|
<div class="md:w-1/2 mb-8 md:mb-0 md:pr-6">
|
||||||
<h1
|
<h1
|
||||||
class="text-3xl sm:text-4xl md:text-5xl font-bold mb-4 sm:mb-6 text-gray-900 leading-tight"
|
class="text-3xl sm:text-4xl md:text-5xl font-bold mb-4 sm:mb-6 text-text leading-tight"
|
||||||
>
|
>
|
||||||
Welcome to <span class="text-blue-600">SecureWeb</span>
|
Welcome to <span class="text-primary-600"
|
||||||
|
>SecureWeb</span
|
||||||
|
>
|
||||||
</h1>
|
</h1>
|
||||||
<p
|
<p
|
||||||
class="text-lg sm:text-xl text-gray-600 max-w-2xl mb-6 sm:mb-8 leading-relaxed"
|
class="text-lg sm:text-xl text-text-secondary max-w-2xl mb-6 sm:mb-8 leading-relaxed"
|
||||||
>
|
>
|
||||||
A comprehensive platform for secure and reliable web
|
A comprehensive platform for secure and reliable web
|
||||||
solutions designed for modern businesses.
|
solutions designed for modern businesses.
|
||||||
@ -35,11 +37,11 @@
|
|||||||
class="flex flex-col xs:flex-row justify-center md:justify-start gap-3 sm:gap-4"
|
class="flex flex-col xs:flex-row justify-center md:justify-start gap-3 sm:gap-4"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="bg-blue-600 hover:bg-blue-700 text-white px-6 sm:px-8 py-2.5 sm:py-3 rounded-md font-medium transition-colors duration-200 w-full xs:w-auto"
|
class="bg-primary-600 hover:bg-primary-700 text-white px-6 sm:px-8 py-2.5 sm:py-3 rounded-md font-medium transition-colors duration-200 w-full xs:w-auto"
|
||||||
>Get Started</button
|
>Get Started</button
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="bg-gray-100 hover:bg-gray-200 text-gray-800 px-6 sm:px-8 py-2.5 sm:py-3 rounded-md font-medium border border-gray-300 transition-colors duration-200 w-full xs:w-auto"
|
class="bg-background-secondary hover:bg-background text-text px-6 sm:px-8 py-2.5 sm:py-3 rounded-md font-medium border border-border transition-colors duration-200 w-full xs:w-auto"
|
||||||
>Learn More</button
|
>Learn More</button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
@ -59,11 +61,13 @@
|
|||||||
<section class="mb-12 sm:mb-16 md:mb-20">
|
<section class="mb-12 sm:mb-16 md:mb-20">
|
||||||
<div class="text-center mb-8 sm:mb-12">
|
<div class="text-center mb-8 sm:mb-12">
|
||||||
<h2
|
<h2
|
||||||
class="text-2xl sm:text-3xl font-bold mb-3 sm:mb-4 text-gray-900"
|
class="text-2xl sm:text-3xl font-bold mb-3 sm:mb-4 text-text"
|
||||||
>
|
>
|
||||||
Our Features
|
Our Features
|
||||||
</h2>
|
</h2>
|
||||||
<p class="text-lg sm:text-xl text-gray-600 max-w-3xl mx-auto">
|
<p
|
||||||
|
class="text-lg sm:text-xl text-text-secondary max-w-3xl mx-auto"
|
||||||
|
>
|
||||||
Discover what makes SecureWeb the preferred choice for
|
Discover what makes SecureWeb the preferred choice for
|
||||||
security-conscious businesses.
|
security-conscious businesses.
|
||||||
</p>
|
</p>
|
||||||
@ -73,20 +77,22 @@
|
|||||||
class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-6 sm:gap-8 md:gap-10"
|
class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-6 sm:gap-8 md:gap-10"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="bg-gray-50 p-6 sm:p-8 rounded-xl shadow-md hover:shadow-lg transition-shadow border border-gray-100"
|
class="bg-background p-6 sm:p-8 rounded-xl shadow-md hover:shadow-lg transition-shadow border border-border"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="bg-blue-100 p-3 rounded-full w-12 h-12 sm:w-14 sm:h-14 flex items-center justify-center mb-4 sm:mb-6"
|
class="bg-primary-100 p-3 rounded-full w-12 h-12 sm:w-14 sm:h-14 flex items-center justify-center mb-4 sm:mb-6"
|
||||||
>
|
>
|
||||||
<Shield class="h-6 w-6 sm:h-7 sm:w-7 text-blue-600" />
|
<Shield
|
||||||
|
class="h-6 w-6 sm:h-7 sm:w-7 text-primary-600"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<h3
|
<h3
|
||||||
class="text-lg sm:text-xl font-bold mb-3 sm:mb-4 text-gray-800"
|
class="text-lg sm:text-xl font-bold mb-3 sm:mb-4 text-text"
|
||||||
>
|
>
|
||||||
Advanced Security
|
Advanced Security
|
||||||
</h3>
|
</h3>
|
||||||
<p
|
<p
|
||||||
class="text-gray-600 leading-relaxed text-sm sm:text-base"
|
class="text-text-secondary leading-relaxed text-sm sm:text-base"
|
||||||
>
|
>
|
||||||
State-of-the-art encryption and security protocols to
|
State-of-the-art encryption and security protocols to
|
||||||
keep your data protected from threats.
|
keep your data protected from threats.
|
||||||
@ -94,20 +100,20 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="bg-white p-6 sm:p-8 rounded-xl shadow-md hover:shadow-lg transition-shadow border border-gray-100"
|
class="bg-background-secondary p-6 sm:p-8 rounded-xl shadow-md hover:shadow-lg transition-shadow border border-border"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="bg-blue-100 p-3 rounded-full w-12 h-12 sm:w-14 sm:h-14 flex items-center justify-center mb-4 sm:mb-6"
|
class="bg-primary-100 p-3 rounded-full w-12 h-12 sm:w-14 sm:h-14 flex items-center justify-center mb-4 sm:mb-6"
|
||||||
>
|
>
|
||||||
<Zap class="h-6 w-6 sm:h-7 sm:w-7 text-blue-600" />
|
<Zap class="h-6 w-6 sm:h-7 sm:w-7 text-primary-600" />
|
||||||
</div>
|
</div>
|
||||||
<h3
|
<h3
|
||||||
class="text-lg sm:text-xl font-bold mb-3 sm:mb-4 text-gray-800"
|
class="text-lg sm:text-xl font-bold mb-3 sm:mb-4 text-text"
|
||||||
>
|
>
|
||||||
Lightning Performance
|
Lightning Performance
|
||||||
</h3>
|
</h3>
|
||||||
<p
|
<p
|
||||||
class="text-gray-600 leading-relaxed text-sm sm:text-base"
|
class="text-text-secondary leading-relaxed text-sm sm:text-base"
|
||||||
>
|
>
|
||||||
Optimized for speed and efficiency, ensuring a smooth
|
Optimized for speed and efficiency, ensuring a smooth
|
||||||
and responsive user experience.
|
and responsive user experience.
|
||||||
@ -115,22 +121,22 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="bg-gray-50 p-6 sm:p-8 rounded-xl shadow-md hover:shadow-lg transition-shadow border border-gray-100 sm:col-span-2 md:col-span-1"
|
class="bg-background p-6 sm:p-8 rounded-xl shadow-md hover:shadow-lg transition-shadow border border-border sm:col-span-2 md:col-span-1"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="bg-blue-100 p-3 rounded-full w-12 h-12 sm:w-14 sm:h-14 flex items-center justify-center mb-4 sm:mb-6"
|
class="bg-primary-100 p-3 rounded-full w-12 h-12 sm:w-14 sm:h-14 flex items-center justify-center mb-4 sm:mb-6"
|
||||||
>
|
>
|
||||||
<Smartphone
|
<Smartphone
|
||||||
class="h-6 w-6 sm:h-7 sm:w-7 text-blue-600"
|
class="h-6 w-6 sm:h-7 sm:w-7 text-primary-600"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<h3
|
<h3
|
||||||
class="text-lg sm:text-xl font-bold mb-3 sm:mb-4 text-gray-800"
|
class="text-lg sm:text-xl font-bold mb-3 sm:mb-4 text-text"
|
||||||
>
|
>
|
||||||
Responsive Design
|
Responsive Design
|
||||||
</h3>
|
</h3>
|
||||||
<p
|
<p
|
||||||
class="text-gray-600 leading-relaxed text-sm sm:text-base"
|
class="text-text-secondary leading-relaxed text-sm sm:text-base"
|
||||||
>
|
>
|
||||||
Fully responsive layouts that provide a seamless
|
Fully responsive layouts that provide a seamless
|
||||||
experience across all devices and screen sizes.
|
experience across all devices and screen sizes.
|
||||||
@ -142,7 +148,7 @@
|
|||||||
<!-- CTA Section -->
|
<!-- CTA Section -->
|
||||||
<section>
|
<section>
|
||||||
<div
|
<div
|
||||||
class="bg-gradient-to-r from-blue-600 to-blue-800 rounded-xl sm:rounded-2xl p-6 sm:p-8 md:p-10 text-white shadow-lg sm:shadow-xl"
|
class="bg-gradient-to-r from-primary-600 to-primary-800 rounded-xl sm:rounded-2xl p-6 sm:p-8 md:p-10 text-white shadow-lg sm:shadow-xl"
|
||||||
>
|
>
|
||||||
<div class="md:flex md:items-center md:justify-between">
|
<div class="md:flex md:items-center md:justify-between">
|
||||||
<div class="mb-6 md:mb-0 md:w-2/3 md:pr-6">
|
<div class="mb-6 md:mb-0 md:w-2/3 md:pr-6">
|
||||||
@ -150,7 +156,7 @@
|
|||||||
Ready to Get Started?
|
Ready to Get Started?
|
||||||
</h2>
|
</h2>
|
||||||
<p
|
<p
|
||||||
class="text-base sm:text-lg text-blue-100 mb-0 max-w-2xl"
|
class="text-base sm:text-lg text-primary-100 mb-0 max-w-2xl"
|
||||||
>
|
>
|
||||||
Join thousands of satisfied users who trust
|
Join thousands of satisfied users who trust
|
||||||
SecureWeb for their web security needs. Sign up
|
SecureWeb for their web security needs. Sign up
|
||||||
@ -159,7 +165,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex justify-center md:justify-end">
|
<div class="flex justify-center md:justify-end">
|
||||||
<button
|
<button
|
||||||
class="bg-white text-blue-700 hover:bg-blue-50 px-6 sm:px-8 py-2.5 sm:py-3 rounded-lg font-semibold text-base sm:text-lg shadow-md transition-colors w-full xs:w-auto"
|
class="bg-background-secondary text-primary-700 hover:bg-primary-50 px-6 sm:px-8 py-2.5 sm:py-3 rounded-lg font-semibold text-base sm:text-lg shadow-md transition-colors w-full xs:w-auto"
|
||||||
>
|
>
|
||||||
Sign Up Now
|
Sign Up Now
|
||||||
</button>
|
</button>
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
import NavDataProvider from "./NavDataProvider.svelte";
|
import NavDataProvider from "./NavDataProvider.svelte";
|
||||||
import Home from "./Home.svelte";
|
import Home from "./Home.svelte";
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
|
import ThemeProvider from "../lib/theme/ThemeProvider.svelte";
|
||||||
|
|
||||||
let sidebarVisible = true;
|
let sidebarVisible = true;
|
||||||
let isMobile = false;
|
let isMobile = false;
|
||||||
@ -53,46 +54,43 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex flex-col min-h-screen bg-gray-50">
|
<ThemeProvider>
|
||||||
<Navbar {toggleSidebar} {isMobile} {sidebarVisible} />
|
<div class="flex flex-col min-h-screen bg-background">
|
||||||
|
<Navbar {toggleSidebar} {isMobile} {sidebarVisible} />
|
||||||
|
|
||||||
<div class="flex flex-1 pt-16 relative">
|
<div class="flex flex-1 pt-16 relative">
|
||||||
<!-- Overlay for mobile sidebar -->
|
<!-- Overlay for mobile sidebar -->
|
||||||
{#if sidebarVisible && isMobile}
|
{#if sidebarVisible && isMobile}
|
||||||
<div
|
|
||||||
class="fixed inset-0 bg-gray-900 bg-opacity-50 z-20 transition-opacity duration-300"
|
|
||||||
on:click={toggleSidebar}
|
|
||||||
aria-hidden="true"
|
|
||||||
></div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<NavDataProvider let:navData>
|
|
||||||
<!-- Sidebar with improved mobile handling -->
|
|
||||||
<Sidebar
|
|
||||||
{navData}
|
|
||||||
onNavItemClick={handleNavItemClick}
|
|
||||||
visible={sidebarVisible}
|
|
||||||
{isMobile}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<main
|
|
||||||
class={`flex-1 transition-all duration-300 ${
|
|
||||||
sidebarVisible && !isMobile ? "md:ml-64" : "ml-0"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
class="container mx-auto px-3 sm:px-4 py-4 sm:py-8 min-h-[calc(100vh-12rem)]"
|
class="fixed inset-0 bg-text bg-opacity-50 z-20 transition-opacity duration-300"
|
||||||
|
on:click={toggleSidebar}
|
||||||
|
aria-hidden="true"
|
||||||
|
></div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<NavDataProvider let:navData>
|
||||||
|
<!-- Sidebar with improved mobile handling -->
|
||||||
|
<Sidebar
|
||||||
|
{navData}
|
||||||
|
onNavItemClick={handleNavItemClick}
|
||||||
|
visible={sidebarVisible}
|
||||||
|
{isMobile}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<main
|
||||||
|
class={`flex-1 transition-all duration-300 ${
|
||||||
|
sidebarVisible && !isMobile ? "md:ml-64" : "ml-0"
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
{#if selectedContentPath}
|
{#if selectedContentPath}
|
||||||
<Home contentPath={selectedContentPath} />
|
<Home contentPath={selectedContentPath} />
|
||||||
{:else}
|
{:else}
|
||||||
<slot />
|
<slot />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</main>
|
||||||
</main>
|
</NavDataProvider>
|
||||||
</NavDataProvider>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Footer is now outside the main content area -->
|
<Footer />
|
||||||
<Footer />
|
</div>
|
||||||
</div>
|
</ThemeProvider>
|
||||||
|
@ -94,13 +94,13 @@
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
.markdown-content {
|
.markdown-content {
|
||||||
padding: 0.5rem;
|
/* padding: 0.5rem; */
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 640px) {
|
@media (min-width: 640px) {
|
||||||
.markdown-content {
|
.markdown-content {
|
||||||
padding: 1rem;
|
/* padding: 1rem; */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,14 +118,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.error {
|
.error {
|
||||||
color: #e53e3e;
|
color: rgb(229 62 62);
|
||||||
}
|
}
|
||||||
|
|
||||||
.content :global(h1) {
|
.content :global(h1) {
|
||||||
font-size: 1.75rem;
|
font-size: 1.75rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
color: #1e40af;
|
color: rgb(var(--color-primary-700));
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,7 +134,7 @@
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
margin-top: 1.5rem;
|
margin-top: 1.5rem;
|
||||||
margin-bottom: 0.75rem;
|
margin-bottom: 0.75rem;
|
||||||
color: #1e3a8a;
|
color: rgb(var(--color-primary-800));
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,7 +143,7 @@
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
margin-top: 1.25rem;
|
margin-top: 1.25rem;
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
color: #1e3a8a;
|
color: rgb(var(--color-primary-800));
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,7 +163,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.content :global(a) {
|
.content :global(a) {
|
||||||
color: #2563eb;
|
color: rgb(var(--color-primary-600));
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
@ -173,16 +173,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.content :global(blockquote) {
|
.content :global(blockquote) {
|
||||||
border-left: 4px solid #e5e7eb;
|
border-left: 4px solid rgb(var(--color-border));
|
||||||
padding-left: 1rem;
|
padding-left: 1rem;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
color: #4b5563;
|
color: rgb(var(--color-text-secondary));
|
||||||
}
|
}
|
||||||
|
|
||||||
.content :global(code) {
|
.content :global(code) {
|
||||||
background-color: #f3f4f6;
|
background-color: rgb(var(--color-background-secondary));
|
||||||
padding: 0.2rem 0.4rem;
|
padding: 0.2rem 0.4rem;
|
||||||
border-radius: 0.25rem;
|
border-radius: 0.25rem;
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
@ -192,7 +192,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.content :global(pre) {
|
.content :global(pre) {
|
||||||
background-color: #f3f4f6;
|
background-color: rgb(var(--color-background-secondary));
|
||||||
padding: 0.75rem;
|
padding: 0.75rem;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
@ -216,7 +216,7 @@
|
|||||||
|
|
||||||
.content :global(hr) {
|
.content :global(hr) {
|
||||||
border: 0;
|
border: 0;
|
||||||
border-top: 1px solid #e5e7eb;
|
border-top: 1px solid rgb(var(--color-border));
|
||||||
margin: 1.5rem 0;
|
margin: 1.5rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,13 +230,13 @@
|
|||||||
|
|
||||||
.content :global(th),
|
.content :global(th),
|
||||||
.content :global(td) {
|
.content :global(td) {
|
||||||
border: 1px solid #e5e7eb;
|
border: 1px solid rgb(var(--color-border));
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content :global(th) {
|
.content :global(th) {
|
||||||
background-color: #f9fafb;
|
background-color: rgb(var(--color-background-secondary));
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 640px) {
|
@media (min-width: 640px) {
|
||||||
|
@ -41,15 +41,9 @@
|
|||||||
>
|
>
|
||||||
<div class="tree-line" style="width: {indentation}px;"></div>
|
<div class="tree-line" style="width: {indentation}px;"></div>
|
||||||
<div class="icon-container">
|
<div class="icon-container">
|
||||||
{#if isExpanded}
|
<ChevronRight
|
||||||
<ChevronDown
|
class="chevron-icon {isExpanded ? 'expanded' : ''}"
|
||||||
class="chevron-icon {isExpanded ? 'expanded' : ''}"
|
/>
|
||||||
/>
|
|
||||||
{:else}
|
|
||||||
<ChevronRight
|
|
||||||
class="chevron-icon {isExpanded ? 'expanded' : ''}"
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
<span class="label">{item.label}</span>
|
<span class="label">{item.label}</span>
|
||||||
</button>
|
</button>
|
||||||
@ -101,7 +95,7 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
border: none;
|
border: none;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: #4b5563;
|
color: var(--color-text-secondary);
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition:
|
transition:
|
||||||
@ -110,12 +104,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.nav-button:hover {
|
.nav-button:hover {
|
||||||
background-color: rgba(0, 0, 0, 0.05);
|
background-color: rgba(var(--color-background), 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-button.active {
|
.nav-button.active {
|
||||||
background-color: rgba(59, 130, 246, 0.1);
|
background-color: rgba(var(--color-primary-500), 0.1);
|
||||||
color: #3b82f6;
|
color: rgb(var(--color-primary-600));
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,7 +128,7 @@
|
|||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
width: 1px;
|
width: 1px;
|
||||||
background-color: #e5e7eb;
|
background-color: rgb(var(--color-border));
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-container {
|
.icon-container {
|
||||||
@ -143,13 +137,13 @@
|
|||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chevron-icon {
|
:global(.chevron-icon) {
|
||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
transition: transform 0.2s;
|
transition: transform 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chevron-icon.expanded {
|
:global(.chevron-icon.expanded) {
|
||||||
transform: rotate(90deg);
|
transform: rotate(90deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Menu, Search, User, Bell, X } from "lucide-svelte";
|
import { Menu, Search, X } from "lucide-svelte";
|
||||||
|
import ThemeToggle from "../lib/theme/ThemeToggle.svelte";
|
||||||
|
|
||||||
export let toggleSidebar: () => void = () => {};
|
export let toggleSidebar: () => void = () => {};
|
||||||
export let isMobile: boolean = false;
|
export let isMobile: boolean = false;
|
||||||
@ -7,11 +8,11 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<header
|
<header
|
||||||
class="bg-gray-50 border-b border-gray-200 fixed top-0 left-0 right-0 z-30 h-16 flex items-center justify-between px-3 sm:px-4 shadow-sm"
|
class="bg-background border-b border-border fixed top-0 left-0 right-0 z-30 h-16 flex items-center justify-between px-3 sm:px-4 shadow-sm"
|
||||||
>
|
>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<button
|
<button
|
||||||
class="mr-3 p-2 rounded-md hover:bg-gray-100 text-gray-700"
|
class="mr-3 p-2 rounded-md hover:bg-background-secondary text-text"
|
||||||
on:click={toggleSidebar}
|
on:click={toggleSidebar}
|
||||||
aria-label={sidebarVisible ? "Close sidebar" : "Open sidebar"}
|
aria-label={sidebarVisible ? "Close sidebar" : "Open sidebar"}
|
||||||
>
|
>
|
||||||
@ -21,13 +22,15 @@
|
|||||||
<Menu class="h-5 w-5" />
|
<Menu class="h-5 w-5" />
|
||||||
{/if}
|
{/if}
|
||||||
</button>
|
</button>
|
||||||
<div class="text-lg sm:text-xl font-bold text-blue-800">SecureWeb</div>
|
<div class="text-lg sm:text-xl font-bold text-primary-600">
|
||||||
|
SecureWeb
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center space-x-2 sm:space-x-4">
|
<div class="flex items-center space-x-2 sm:space-x-4">
|
||||||
<!-- Search button for mobile -->
|
<!-- Search button for mobile -->
|
||||||
<button
|
<button
|
||||||
class="md:hidden p-2 rounded-md hover:bg-gray-100 text-gray-700"
|
class="md:hidden p-2 rounded-md hover:bg-background-secondary text-text"
|
||||||
>
|
>
|
||||||
<Search class="h-5 w-5" />
|
<Search class="h-5 w-5" />
|
||||||
</button>
|
</button>
|
||||||
@ -35,26 +38,16 @@
|
|||||||
<!-- Search bar for desktop -->
|
<!-- Search bar for desktop -->
|
||||||
<div class="relative hidden md:block">
|
<div class="relative hidden md:block">
|
||||||
<Search
|
<Search
|
||||||
class="h-4 w-4 absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"
|
class="h-4 w-4 absolute left-3 top-1/2 transform -translate-y-1/2 text-text-muted"
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Search..."
|
placeholder="Search..."
|
||||||
class="pl-9 pr-4 py-1.5 text-sm rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent w-40 lg:w-64"
|
class="pl-9 pr-4 py-1.5 text-sm rounded-md border border-border bg-background text-text focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent w-40 lg:w-64"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<!-- Theme toggle -->
|
||||||
class="p-2 rounded-full hover:bg-gray-100 text-gray-700 relative hidden sm:block"
|
<ThemeToggle class="hover:bg-background-secondary" />
|
||||||
>
|
|
||||||
<Bell class="h-5 w-5" />
|
|
||||||
<span
|
|
||||||
class="absolute top-1 right-1 w-2 h-2 bg-blue-500 rounded-full"
|
|
||||||
></span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button class="p-1.5 rounded-full hover:bg-gray-100 text-gray-700">
|
|
||||||
<User class="h-5 w-5" />
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
@ -75,25 +75,11 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<aside
|
<aside
|
||||||
class="sidebar bg-white border-gray-200 h-screen fixed top-0 left-0 pt-16 overflow-y-auto shadow-sm z-20
|
class="sidebar bg-background-secondary border-border h-screen fixed top-0 left-0 pt-16 overflow-y-auto shadow-sm z-20
|
||||||
{isMobile ? 'w-[85%] max-w-xs' : 'w-64'}
|
{isMobile ? 'w-[85%] max-w-xs' : 'w-64'}
|
||||||
transition-transform duration-300 ease-in-out
|
transition-transform duration-300 ease-in-out
|
||||||
{visible ? 'translate-x-0' : '-translate-x-full'}"
|
{visible ? 'translate-x-0' : '-translate-x-full'}"
|
||||||
>
|
>
|
||||||
<div
|
|
||||||
class="sidebar-header border-gray-200 p-3 flex justify-between items-center"
|
|
||||||
>
|
|
||||||
{#if isMobile}
|
|
||||||
<button
|
|
||||||
class="p-1 rounded-md hover:bg-gray-100"
|
|
||||||
on:click={toggleSidebar}
|
|
||||||
aria-label="Close sidebar"
|
|
||||||
>
|
|
||||||
<X class="w-5 h-5 text-gray-500" />
|
|
||||||
</button>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<nav class="w-full py-2">
|
<nav class="w-full py-2">
|
||||||
{#each navData as item}
|
{#each navData as item}
|
||||||
<NavItemComponent
|
<NavItemComponent
|
||||||
@ -110,11 +96,11 @@
|
|||||||
|
|
||||||
{#if isMobile && !visible}
|
{#if isMobile && !visible}
|
||||||
<button
|
<button
|
||||||
class="fixed top-4 left-4 z-10 p-2 bg-white rounded-md shadow-md hover:bg-gray-100"
|
class="fixed top-4 left-4 z-10 p-2 bg-background-secondary rounded-md shadow-md hover:bg-background"
|
||||||
on:click={toggleSidebar}
|
on:click={toggleSidebar}
|
||||||
aria-label="Open sidebar"
|
aria-label="Open sidebar"
|
||||||
>
|
>
|
||||||
<Menu class="w-5 h-5 text-gray-700" />
|
<Menu class="w-5 h-5 text-text" />
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
@ -122,6 +108,7 @@
|
|||||||
.sidebar {
|
.sidebar {
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
scrollbar-color: rgba(156, 163, 175, 0.5) transparent;
|
scrollbar-color: rgba(156, 163, 175, 0.5) transparent;
|
||||||
|
border-right: 1px solid rgb(var(--color-border));
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar::-webkit-scrollbar {
|
.sidebar::-webkit-scrollbar {
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
let count: number = $state(0)
|
|
||||||
const increment = () => {
|
|
||||||
count += 1
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<button onclick={increment}>
|
|
||||||
count is {count}
|
|
||||||
</button>
|
|
104
sweb/src/lib/theme/ThemeProvider.svelte
Normal file
104
sweb/src/lib/theme/ThemeProvider.svelte
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
<script lang="ts" module>
|
||||||
|
import { createContext } from "../create-context";
|
||||||
|
|
||||||
|
// Define theme types
|
||||||
|
export type Theme = "light" | "dark" | "system";
|
||||||
|
|
||||||
|
// Create theme context
|
||||||
|
export const { get: getThemeContext, set: setThemeContext } =
|
||||||
|
createContext<{
|
||||||
|
theme: Theme;
|
||||||
|
setTheme: (theme: Theme) => void;
|
||||||
|
subscribe?: (callback: (theme: Theme) => void) => () => void;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
|
||||||
|
// Props
|
||||||
|
export let initialTheme: Theme = "system";
|
||||||
|
|
||||||
|
// State with reactivity
|
||||||
|
let theme = initialTheme;
|
||||||
|
let mounted = false;
|
||||||
|
|
||||||
|
// Create a custom store for the theme
|
||||||
|
const themeStore = {
|
||||||
|
subscribe: (callback: (theme: Theme) => void) => {
|
||||||
|
// Initial call
|
||||||
|
callback(theme);
|
||||||
|
|
||||||
|
// Setup a MutationObserver to watch for class changes on documentElement
|
||||||
|
const observer = new MutationObserver(() => {
|
||||||
|
callback(theme);
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(document.documentElement, {
|
||||||
|
attributes: true,
|
||||||
|
attributeFilter: ["class"],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Return unsubscribe function
|
||||||
|
return () => observer.disconnect();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set the theme in localStorage and update the DOM
|
||||||
|
function setTheme(newTheme: Theme) {
|
||||||
|
theme = newTheme;
|
||||||
|
|
||||||
|
if (mounted) {
|
||||||
|
updateTheme(theme);
|
||||||
|
localStorage.setItem("theme", theme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the DOM based on the current theme
|
||||||
|
function updateTheme(currentTheme: Theme) {
|
||||||
|
const isDark =
|
||||||
|
currentTheme === "dark" ||
|
||||||
|
(currentTheme === "system" &&
|
||||||
|
window.matchMedia("(prefers-color-scheme: dark)").matches);
|
||||||
|
|
||||||
|
document.documentElement.classList.toggle("dark", isDark);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize theme on mount
|
||||||
|
onMount(() => {
|
||||||
|
// Get stored theme or use system preference
|
||||||
|
const storedTheme = localStorage.getItem("theme") as Theme | null;
|
||||||
|
if (storedTheme) {
|
||||||
|
theme = storedTheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up system theme change listener
|
||||||
|
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
||||||
|
const handleChange = () => {
|
||||||
|
if (theme === "system") {
|
||||||
|
updateTheme("system");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
mediaQuery.addEventListener("change", handleChange);
|
||||||
|
|
||||||
|
// Apply initial theme
|
||||||
|
updateTheme(theme);
|
||||||
|
mounted = true;
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
mediaQuery.removeEventListener("change", handleChange);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set the context with the store
|
||||||
|
setThemeContext({
|
||||||
|
get theme() {
|
||||||
|
return theme;
|
||||||
|
},
|
||||||
|
setTheme,
|
||||||
|
subscribe: themeStore.subscribe,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<slot />
|
78
sweb/src/lib/theme/ThemeToggle.svelte
Normal file
78
sweb/src/lib/theme/ThemeToggle.svelte
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Sun, Moon } from "lucide-svelte";
|
||||||
|
import { getThemeContext, type Theme } from "./ThemeProvider.svelte";
|
||||||
|
import { cn } from "../utils";
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
|
||||||
|
export let size: "sm" | "md" | "lg" = "md";
|
||||||
|
|
||||||
|
let className: string | undefined | null = undefined;
|
||||||
|
export { className as class };
|
||||||
|
|
||||||
|
const themeContext = getThemeContext();
|
||||||
|
|
||||||
|
// Create a reactive variable to track the current theme
|
||||||
|
let currentTheme: Theme = themeContext.theme;
|
||||||
|
|
||||||
|
// Subscribe to theme changes if the subscribe method is available
|
||||||
|
onMount(() => {
|
||||||
|
// Set initial theme
|
||||||
|
currentTheme = themeContext.theme;
|
||||||
|
|
||||||
|
// If subscribe is available, use it to update the theme
|
||||||
|
if (themeContext.subscribe) {
|
||||||
|
const unsubscribe = themeContext.subscribe((newTheme) => {
|
||||||
|
currentTheme = newTheme;
|
||||||
|
});
|
||||||
|
|
||||||
|
return unsubscribe;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Size classes for the button
|
||||||
|
const sizeClasses = {
|
||||||
|
sm: "h-8 w-8",
|
||||||
|
md: "h-9 w-9",
|
||||||
|
lg: "h-10 w-10",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Size classes for the icons
|
||||||
|
const iconSizeClasses = {
|
||||||
|
sm: "h-4 w-4",
|
||||||
|
md: "h-5 w-5",
|
||||||
|
lg: "h-6 w-6",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Toggle between light and dark modes only
|
||||||
|
function toggleTheme() {
|
||||||
|
let newTheme: Theme;
|
||||||
|
|
||||||
|
// Just toggle between light and dark
|
||||||
|
if (currentTheme === "light") {
|
||||||
|
newTheme = "dark";
|
||||||
|
} else {
|
||||||
|
newTheme = "light";
|
||||||
|
}
|
||||||
|
|
||||||
|
themeContext.setTheme(newTheme);
|
||||||
|
// Update our local state immediately
|
||||||
|
currentTheme = newTheme;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
aria-label="Toggle theme"
|
||||||
|
class={cn(
|
||||||
|
"rounded-md p-2 transition-colors hover:bg-background-secondary",
|
||||||
|
sizeClasses[size],
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
on:click={toggleTheme}
|
||||||
|
>
|
||||||
|
{#if currentTheme === "dark"}
|
||||||
|
<Sun class={cn("text-text", iconSizeClasses[size])} />
|
||||||
|
{:else}
|
||||||
|
<Moon class={cn("text-text", iconSizeClasses[size])} />
|
||||||
|
{/if}
|
||||||
|
</button>
|
@ -3,6 +3,7 @@ export default {
|
|||||||
content: [
|
content: [
|
||||||
'./src/**/*.{html,js,svelte,ts}',
|
'./src/**/*.{html,js,svelte,ts}',
|
||||||
],
|
],
|
||||||
|
darkMode: 'class',
|
||||||
theme: {
|
theme: {
|
||||||
screens: {
|
screens: {
|
||||||
'xs': '480px',
|
'xs': '480px',
|
||||||
@ -14,6 +15,32 @@ export default {
|
|||||||
},
|
},
|
||||||
extend: {
|
extend: {
|
||||||
colors: {
|
colors: {
|
||||||
|
primary: {
|
||||||
|
50: 'rgb(var(--color-primary-50) / <alpha-value>)',
|
||||||
|
100: 'rgb(var(--color-primary-100) / <alpha-value>)',
|
||||||
|
200: 'rgb(var(--color-primary-200) / <alpha-value>)',
|
||||||
|
300: 'rgb(var(--color-primary-300) / <alpha-value>)',
|
||||||
|
400: 'rgb(var(--color-primary-400) / <alpha-value>)',
|
||||||
|
500: 'rgb(var(--color-primary-500) / <alpha-value>)',
|
||||||
|
600: 'rgb(var(--color-primary-600) / <alpha-value>)',
|
||||||
|
700: 'rgb(var(--color-primary-700) / <alpha-value>)',
|
||||||
|
800: 'rgb(var(--color-primary-800) / <alpha-value>)',
|
||||||
|
900: 'rgb(var(--color-primary-900) / <alpha-value>)',
|
||||||
|
},
|
||||||
|
background: {
|
||||||
|
DEFAULT: 'rgb(var(--color-background) / <alpha-value>)',
|
||||||
|
secondary: 'rgb(var(--color-background-secondary) / <alpha-value>)',
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
DEFAULT: 'rgb(var(--color-text) / <alpha-value>)',
|
||||||
|
secondary: 'rgb(var(--color-text-secondary) / <alpha-value>)',
|
||||||
|
muted: 'rgb(var(--color-text-muted) / <alpha-value>)',
|
||||||
|
},
|
||||||
|
border: {
|
||||||
|
DEFAULT: 'rgb(var(--color-border) / <alpha-value>)',
|
||||||
|
secondary: 'rgb(var(--color-border-secondary) / <alpha-value>)',
|
||||||
|
},
|
||||||
|
// Keep original colors for backward compatibility
|
||||||
blue: {
|
blue: {
|
||||||
600: '#2563eb',
|
600: '#2563eb',
|
||||||
700: '#1d4ed8',
|
700: '#1d4ed8',
|
||||||
|
Loading…
Reference in New Issue
Block a user