Browse Source

theme defined, with IBM Plex Serif/Mono font

main
Silberengel 4 weeks ago
parent
commit
707caf3962
  1. 38
      src/app.css
  2. 12
      src/app.html
  3. 92
      src/lib/components/ThemeToggle.svelte
  4. 37
      src/routes/+layout.svelte
  5. 2
      src/routes/+page.svelte

38
src/app.css

@ -78,6 +78,9 @@
--warning-text: #fbbf24; --warning-text: #fbbf24;
} }
/* Import fonts - IBM Plex Serif for classic Roman feel with modern tech aesthetic */
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Serif:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500;1,600;1,700&family=IBM+Plex+Mono:wght@400;500;600;700&display=swap');
/* Base styles */ /* Base styles */
* { * {
box-sizing: border-box; box-sizing: border-box;
@ -86,28 +89,53 @@
body { body {
margin: 0; margin: 0;
padding: 0; padding: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', font-family: 'IBM Plex Serif', 'Georgia', 'Times New Roman', serif;
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
background-color: var(--bg-primary); background-color: var(--bg-primary);
color: var(--text-primary); color: var(--text-primary);
transition: background-color 0.3s ease, color 0.3s ease; transition: background-color 0.3s ease, color 0.3s ease;
line-height: 1.6;
}
/* Monospace font for code */
code, pre, .mono {
font-family: 'IBM Plex Mono', 'Courier New', monospace;
}
/* Headings with slightly more weight */
h1, h2, h3, h4, h5, h6 {
font-weight: 600;
letter-spacing: -0.02em;
} }
/* Theme toggle utility */ /* Theme toggle button */
.theme-toggle { .theme-toggle {
cursor: pointer; cursor: pointer;
padding: 0.5rem; padding: 0.5rem 0.75rem;
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
border-radius: 0.375rem; border-radius: 0.375rem;
background: var(--card-bg); background: var(--card-bg);
color: var(--text-primary); color: var(--text-primary);
transition: all 0.2s ease; transition: all 0.2s ease;
font-size: 0.875rem;
display: inline-flex;
align-items: center;
gap: 0.5rem;
font-family: 'IBM Plex Serif', serif;
} }
.theme-toggle:hover { .theme-toggle:hover {
background: var(--bg-secondary); background: var(--bg-secondary);
border-color: var(--accent); border-color: var(--accent);
color: var(--accent);
}
.theme-toggle:active {
transform: scale(0.98);
}
.theme-toggle-icon {
font-size: 1rem;
line-height: 1;
} }

12
src/app.html

@ -5,6 +5,18 @@
<link rel="icon" href="%sveltekit.assets%/favicon.ico" /> <link rel="icon" href="%sveltekit.assets%/favicon.ico" />
<link rel="apple-touch-icon" href="%sveltekit.assets%/apple-touch-icon.png" /> <link rel="apple-touch-icon" href="%sveltekit.assets%/apple-touch-icon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<script>
// Apply theme immediately to prevent flash
(function() {
const savedTheme = localStorage.getItem('theme');
if (savedTheme === 'light') {
// Light theme is default, no attribute needed
} else {
// Default to dark
document.documentElement.setAttribute('data-theme', 'dark');
}
})();
</script>
%sveltekit.head% %sveltekit.head%
</head> </head>
<body data-sveltekit-preload-data="hover"> <body data-sveltekit-preload-data="hover">

92
src/lib/components/ThemeToggle.svelte

@ -0,0 +1,92 @@
<script lang="ts">
import { getContext } from 'svelte';
import { onMount } from 'svelte';
// Get theme and toggle function from layout context
const themeContext = getContext<{
theme: { value: 'light' | 'dark' };
toggleTheme: () => void;
}>('theme');
let currentTheme: 'light' | 'dark' = 'dark';
function updateTheme() {
currentTheme = document.documentElement.hasAttribute('data-theme') ? 'dark' : 'light';
}
onMount(() => {
// Check current theme from document
updateTheme();
// Watch for theme changes
const observer = new MutationObserver(() => {
updateTheme();
});
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['data-theme']
});
return () => observer.disconnect();
});
function handleToggle() {
if (themeContext) {
themeContext.toggleTheme();
} else {
// Fallback: toggle manually
const isDark = document.documentElement.hasAttribute('data-theme');
if (isDark) {
document.documentElement.removeAttribute('data-theme');
} else {
document.documentElement.setAttribute('data-theme', 'dark');
}
localStorage.setItem('theme', isDark ? 'light' : 'dark');
}
updateTheme();
}
</script>
<button class="theme-toggle" onclick={handleToggle} title="Toggle theme">
<span class="theme-toggle-icon">
{#if currentTheme === 'dark'}
{:else}
🌙
{/if}
</span>
<span>{currentTheme === 'dark' ? 'Light' : 'Dark'}</span>
</button>
<style>
.theme-toggle {
cursor: pointer;
padding: 0.5rem 0.75rem;
border: 1px solid var(--border-color);
border-radius: 0.375rem;
background: var(--card-bg);
color: var(--text-primary);
transition: all 0.2s ease;
font-size: 0.875rem;
display: inline-flex;
align-items: center;
gap: 0.5rem;
font-family: 'IBM Plex Serif', serif;
}
.theme-toggle:hover {
background: var(--bg-secondary);
border-color: var(--accent);
color: var(--accent);
}
.theme-toggle:active {
transform: scale(0.98);
}
.theme-toggle-icon {
font-size: 1rem;
line-height: 1;
}
</style>

37
src/routes/+layout.svelte

@ -1,21 +1,30 @@
<script lang="ts"> <script lang="ts">
import '../app.css'; import '../app.css';
import { onMount } from 'svelte'; import { onMount, setContext } from 'svelte';
// Theme management // Theme management - default to dark
let theme: 'light' | 'dark' = 'light'; let theme: 'light' | 'dark' = 'dark';
onMount(() => { onMount(() => {
// Check for saved theme preference or default to light // Check for saved theme preference or default to dark
const savedTheme = localStorage.getItem('theme') as 'light' | 'dark' | null; const savedTheme = localStorage.getItem('theme') as 'light' | 'dark' | null;
if (savedTheme === 'light' || savedTheme === 'dark') { if (savedTheme === 'light' || savedTheme === 'dark') {
theme = savedTheme; theme = savedTheme;
} else { } else {
// Check system preference // Default to dark
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; theme = 'dark';
theme = prefersDark ? 'dark' : 'light';
} }
applyTheme(); applyTheme();
// Watch for system theme changes (only if user hasn't set a preference)
if (typeof window !== 'undefined') {
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
if (!localStorage.getItem('theme')) {
theme = e.matches ? 'dark' : 'light';
applyTheme();
}
});
}
}); });
function applyTheme() { function applyTheme() {
@ -32,16 +41,10 @@
applyTheme(); applyTheme();
} }
// Watch for system theme changes // Provide theme context to child components
onMount(() => { setContext('theme', {
if (typeof window !== 'undefined') { get theme() { return { value: theme }; },
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => { toggleTheme
if (!localStorage.getItem('theme')) {
theme = e.matches ? 'dark' : 'light';
applyTheme();
}
});
}
}); });
</script> </script>

2
src/routes/+page.svelte

@ -8,6 +8,7 @@
import { nip19 } from 'nostr-tools'; import { nip19 } from 'nostr-tools';
import { getPublicKeyWithNIP07, isNIP07Available } from '../lib/services/nostr/nip07-signer.js'; import { getPublicKeyWithNIP07, isNIP07Available } from '../lib/services/nostr/nip07-signer.js';
import { ForkCountService } from '../lib/services/nostr/fork-count-service.js'; import { ForkCountService } from '../lib/services/nostr/fork-count-service.js';
import ThemeToggle from '../lib/components/ThemeToggle.svelte';
let repos = $state<NostrEvent[]>([]); let repos = $state<NostrEvent[]>([]);
let loading = $state(true); let loading = $state(true);
@ -261,6 +262,7 @@
<a href="/signup">Sign Up</a> <a href="/signup">Sign Up</a>
<a href="/docs/nip34">NIP-34 Docs</a> <a href="/docs/nip34">NIP-34 Docs</a>
<div class="auth-section"> <div class="auth-section">
<ThemeToggle />
{#if userPubkey} {#if userPubkey}
<span class="user-info"> <span class="user-info">
{nip19.npubEncode(userPubkey).slice(0, 16)}... {nip19.npubEncode(userPubkey).slice(0, 16)}...

Loading…
Cancel
Save