added configurable font sizes

This commit is contained in:
2026-02-07 10:05:14 +01:00
parent d83a3e0542
commit 6a633fa867
2 changed files with 74 additions and 31 deletions
+57 -30
View File
@@ -8,19 +8,26 @@
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,wght@0,400;0,500;0,600;0,700;1,400&family=Playfair+Display:wght@600;700&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,wght@0,400;0,500;0,600;0,700;1,400&family=Playfair+Display:wght@600;700&display=swap" rel="stylesheet">
<style> <style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--font-size: 15px;
--title-size: 32px;
--calendar-size: 14px;
--button-size: 14px;
}
body { body {
font-family: 'DM Sans', sans-serif; font-family: 'DM Sans', sans-serif;
background: #F5F0EB; background: #F5F0EB;
color: #3A332D; color: #3A332D;
min-height: 100vh; min-height: 100vh;
padding: 20px 16px; padding: 20px 16px;
font-size: var(--font-size);
} }
.container { max-width: 520px; margin: 0 auto; } .container { max-width: 520px; margin: 0 auto; }
/* Header */ /* Header */
.header { text-align: center; margin-bottom: 28px; } .header { text-align: center; margin-bottom: 28px; }
.header-sub { font-size: 11px; letter-spacing: 0.2em; color: #B0A89E; font-weight: 500; text-transform: uppercase; margin-bottom: 4px; } .header-sub { font-size: calc(var(--font-size) - 2px); letter-spacing: 0.2em; color: #B0A89E; font-weight: 500; text-transform: uppercase; margin-bottom: 4px; }
.header h1 { font-family: 'Playfair Display', serif; font-size: 28px; font-weight: 700; color: #3A332D; line-height: 1.2; } .header h1 { font-family: 'Playfair Display', serif; font-size: var(--title-size); font-weight: 700; color: #3A332D; line-height: 1.2; }
.header-line { width: 40px; height: 2px; background: #C17F59; margin: 10px auto 0; border-radius: 1px; } .header-line { width: 40px; height: 2px; background: #C17F59; margin: 10px auto 0; border-radius: 1px; }
/* User bar */ /* User bar */
@@ -30,8 +37,8 @@
background: #FFF; box-shadow: 0 1px 3px rgba(0,0,0,0.04); background: #FFF; box-shadow: 0 1px 3px rgba(0,0,0,0.04);
} }
.user-bar-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; } .user-bar-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; }
.user-bar-name { font-size: 13px; font-weight: 600; color: #3A332D; } .user-bar-name { font-size: var(--font-size); font-weight: 600; color: #3A332D; }
.user-bar-label { font-size: 11px; color: #B0A89E; } .user-bar-label { font-size: calc(var(--font-size) - 2px); color: #B0A89E; }
.btn-logout { .btn-logout {
margin-left: auto; border: none; background: #F5F0EB; border-radius: 6px; margin-left: auto; border: none; background: #F5F0EB; border-radius: 6px;
padding: 4px 10px; font-size: 11px; font-weight: 500; color: #8B7E74; padding: 4px 10px; font-size: 11px; font-weight: 500; color: #8B7E74;
@@ -41,27 +48,27 @@
/* Cards */ /* Cards */
.card { background: #FFF; border-radius: 14px; padding: 16px; margin-bottom: 14px; box-shadow: 0 1px 3px rgba(0,0,0,0.04); } .card { background: #FFF; border-radius: 14px; padding: 16px; margin-bottom: 14px; box-shadow: 0 1px 3px rgba(0,0,0,0.04); }
.card-title { font-size: 11px; font-weight: 600; color: #8B7E74; margin-bottom: 10px; text-transform: uppercase; letter-spacing: 0.08em; } .card-title { font-size: calc(var(--font-size) - 2px); font-weight: 600; color: #8B7E74; margin-bottom: 10px; text-transform: uppercase; letter-spacing: 0.08em; }
/* Controls */ /* Controls */
.controls { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; } .controls { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.controls select, .controls input { .controls select, .controls input {
padding: 7px 10px; border-radius: 8px; border: 1px solid #E8E0D8; padding: 7px 10px; border-radius: 8px; border: 1px solid #E8E0D8;
background: #FDFBF9; font-size: 13px; font-family: 'DM Sans', sans-serif; background: #FDFBF9; font-size: var(--button-size); font-family: 'DM Sans', sans-serif;
color: #3A332D; flex: 1; min-width: 100px; color: #3A332D; flex: 1; min-width: 100px;
} }
.hint { font-size: 11px; color: #B0A89E; margin-top: 8px; } .hint { font-size: calc(var(--font-size) - 2px); color: #B0A89E; margin-top: 8px; }
/* Calendar */ /* Calendar */
.cal-nav { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; } .cal-nav { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; }
.cal-nav button { .cal-nav button {
border: none; background: #F5F0EB; border-radius: 8px; width: 32px; height: 32px; border: none; background: #F5F0EB; border-radius: 8px; width: 36px; height: 36px;
cursor: pointer; font-size: 16px; color: #5C524A; display: flex; align-items: center; justify-content: center; cursor: pointer; font-size: 18px; color: #5C524A; display: flex; align-items: center; justify-content: center;
} }
.cal-nav button:hover { background: #EAE3DB; } .cal-nav button:hover { background: #EAE3DB; }
.cal-month { font-size: 16px; font-weight: 600; color: #3A332D; } .cal-month { font-size: calc(var(--title-size) * 0.55); font-weight: 600; color: #3A332D; }
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; } .cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.cal-day-header { text-align: center; padding: 6px 0; font-size: 11px; font-weight: 600; color: #8B7E74; letter-spacing: 0.05em; } .cal-day-header { text-align: center; padding: 6px 0; font-size: calc(var(--calendar-size) - 1px); font-weight: 600; color: #8B7E74; letter-spacing: 0.05em; }
.cal-day { .cal-day {
position: relative; min-height: 48px; padding: 3px; border-radius: 6px; position: relative; min-height: 48px; padding: 3px; border-radius: 6px;
background: #FDFBF9; cursor: pointer; border: 1px solid transparent; background: #FDFBF9; cursor: pointer; border: 1px solid transparent;
@@ -71,7 +78,7 @@
.cal-day.empty { background: transparent; cursor: default; } .cal-day.empty { background: transparent; cursor: default; }
.cal-day.today { border: 2px solid #C17F59; } .cal-day.today { border: 2px solid #C17F59; }
.cal-day.in-selection { background: #D4C5B9; } .cal-day.in-selection { background: #D4C5B9; }
.cal-day-num { font-size: 12px; font-weight: 400; color: #5C524A; text-align: right; padding-right: 2px; display: block; } .cal-day-num { font-size: var(--calendar-size); font-weight: 400; color: #5C524A; text-align: right; padding-right: 2px; display: block; }
.cal-day.today .cal-day-num { font-weight: 700; color: #C17F59; } .cal-day.today .cal-day-num { font-weight: 700; color: #C17F59; }
.cal-day-dots { display: flex; flex-wrap: wrap; gap: 2px; margin-top: 2px; } .cal-day-dots { display: flex; flex-wrap: wrap; gap: 2px; margin-top: 2px; }
.dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; } .dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
@@ -79,10 +86,10 @@
/* Legend */ /* Legend */
.legend { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; margin-bottom: 14px; padding: 8px 0; } .legend { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; margin-bottom: 14px; padding: 8px 0; }
.legend-item { display: flex; align-items: center; gap: 5px; font-size: 11px; color: #5C524A; } .legend-item { display: flex; align-items: center; gap: 5px; font-size: calc(var(--font-size) - 2px); color: #5C524A; }
.legend-dot { width: 8px; height: 8px; border-radius: 50%; } .legend-dot { width: 8px; height: 8px; border-radius: 50%; }
.legend-row { width: 100%; display: flex; justify-content: center; gap: 16px; margin-top: 2px; } .legend-row { width: 100%; display: flex; justify-content: center; gap: 16px; margin-top: 2px; }
.legend-row span { font-size: 10px; color: #B0A89E; } .legend-row span { font-size: calc(var(--font-size) - 3px); color: #B0A89E; }
/* Booking list */ /* Booking list */
.booking-item { .booking-item {
@@ -91,20 +98,20 @@
} }
.booking-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; } .booking-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; }
.booking-info { flex: 1; min-width: 0; } .booking-info { flex: 1; min-width: 0; }
.booking-name { font-size: 13px; font-weight: 600; color: #3A332D; } .booking-name { font-size: var(--font-size); font-weight: 600; color: #3A332D; }
.booking-badge { .booking-badge {
display: inline-block; margin-left: 8px; font-size: 10px; font-weight: 500; display: inline-block; margin-left: 8px; font-size: calc(var(--font-size) - 3px); font-weight: 500;
padding: 1px 6px; border-radius: 10px; padding: 1px 6px; border-radius: 10px;
} }
.booking-badge.confirmed { background: #81B29A22; color: #5A9A78; } .booking-badge.confirmed { background: #81B29A22; color: #5A9A78; }
.booking-badge.planned { background: #F2CC8F33; color: #C49A4A; } .booking-badge.planned { background: #F2CC8F33; color: #C49A4A; }
.booking-dates { font-size: 11px; color: #8B7E74; margin-top: 2px; } .booking-dates { font-size: calc(var(--font-size) - 2px); color: #8B7E74; margin-top: 2px; }
.booking-actions button { .booking-actions button {
border: none; background: none; cursor: pointer; font-size: 14px; padding: 4px; border-radius: 4px; border: none; background: none; cursor: pointer; font-size: 14px; padding: 4px; border-radius: 4px;
} }
.booking-actions .toggle { color: #B0A89E; } .booking-actions .toggle { color: #B0A89E; }
.booking-actions .delete { color: #D4756B; } .booking-actions .delete { color: #D4756B; }
.empty-state { text-align: center; padding: 30px; color: #B0A89E; font-style: italic; } .empty-state { text-align: center; padding: 30px; color: #B0A89E; font-style: italic; font-size: var(--font-size); }
/* Comments */ /* Comments */
.comments-list { display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px; max-height: 400px; overflow-y: auto; } .comments-list { display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px; max-height: 400px; overflow-y: auto; }
@@ -113,9 +120,9 @@
border-left: 3px solid #ccc; position: relative; border-left: 3px solid #ccc; position: relative;
} }
.comment-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px; } .comment-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px; }
.comment-author { font-size: 12px; font-weight: 600; } .comment-author { font-size: calc(var(--font-size) - 1px); font-weight: 600; }
.comment-time { font-size: 10px; color: #B0A89E; } .comment-time { font-size: calc(var(--font-size) - 3px); color: #B0A89E; }
.comment-text { font-size: 13px; color: #5C524A; line-height: 1.5; } .comment-text { font-size: var(--font-size); color: #5C524A; line-height: 1.5; }
.comment-delete { .comment-delete {
border: none; background: none; cursor: pointer; font-size: 12px; border: none; background: none; cursor: pointer; font-size: 12px;
color: #D4756B; padding: 2px 4px; border-radius: 4px; margin-left: 8px; color: #D4756B; padding: 2px 4px; border-radius: 4px; margin-left: 8px;
@@ -125,13 +132,13 @@
.comment-form { display: flex; gap: 8px; align-items: flex-end; } .comment-form { display: flex; gap: 8px; align-items: flex-end; }
.comment-form input { .comment-form input {
flex: 1; padding: 8px 12px; border-radius: 8px; border: 1px solid #E8E0D8; flex: 1; padding: 8px 12px; border-radius: 8px; border: 1px solid #E8E0D8;
background: #FFF; font-size: 13px; font-family: 'DM Sans', sans-serif; background: #FFF; font-size: var(--button-size); font-family: 'DM Sans', sans-serif;
color: #3A332D; outline: none; color: #3A332D; outline: none;
} }
.comment-form input:focus { border-color: #C17F59; } .comment-form input:focus { border-color: #C17F59; }
.btn-send { .btn-send {
padding: 8px 16px; border-radius: 8px; border: none; padding: 8px 16px; border-radius: 8px; border: none;
background: #C17F59; color: #FFF; font-size: 12px; font-weight: 600; background: #C17F59; color: #FFF; font-size: var(--button-size); font-weight: 600;
cursor: pointer; font-family: 'DM Sans', sans-serif; white-space: nowrap; cursor: pointer; font-family: 'DM Sans', sans-serif; white-space: nowrap;
} }
.btn-send:hover { background: #A96A4A; } .btn-send:hover { background: #A96A4A; }
@@ -144,7 +151,7 @@
.login-box { text-align: center; width: 280px; } .login-box { text-align: center; width: 280px; }
.login-box input, .login-box select { .login-box input, .login-box select {
padding: 10px 16px; border-radius: 8px; border: 1px solid #E8E0D8; padding: 10px 16px; border-radius: 8px; border: 1px solid #E8E0D8;
font-size: 14px; font-family: 'DM Sans', sans-serif; width: 100%; font-size: var(--button-size); font-family: 'DM Sans', sans-serif; width: 100%;
margin-bottom: 10px; text-align: center; background: #FFF; color: #3A332D; margin-bottom: 10px; text-align: center; background: #FFF; color: #3A332D;
} }
.login-box .btn-send { display: block; width: 100%; padding: 10px; } .login-box .btn-send { display: block; width: 100%; padding: 10px; }
@@ -154,7 +161,7 @@
display: flex; align-items: center; gap: 6px; display: flex; align-items: center; gap: 6px;
padding: 8px 14px; border-radius: 10px; border: 2px solid #E8E0D8; padding: 8px 14px; border-radius: 10px; border: 2px solid #E8E0D8;
background: #FFF; cursor: pointer; font-family: 'DM Sans', sans-serif; background: #FFF; cursor: pointer; font-family: 'DM Sans', sans-serif;
font-size: 13px; font-weight: 500; color: #5C524A; transition: all 0.15s ease; font-size: var(--button-size); font-weight: 500; color: #5C524A; transition: all 0.15s ease;
} }
.login-member-btn:hover { border-color: #C17F59; } .login-member-btn:hover { border-color: #C17F59; }
.login-member-btn.selected { border-color: #C17F59; background: #FDF6F0; font-weight: 600; } .login-member-btn.selected { border-color: #C17F59; background: #FDF6F0; font-weight: 600; }
@@ -167,10 +174,10 @@
<div id="login-screen" class="login-overlay" style="display:none;"> <div id="login-screen" class="login-overlay" style="display:none;">
<div class="login-box"> <div class="login-box">
<div class="header-sub">Révfülöp · Balaton</div> <div class="header-sub site-subtitle">Révfülöp · Balaton</div>
<h1 style="font-family:'Playfair Display',serif;font-size:28px;font-weight:700;color:#3A332D;margin:4px 0 8px;">Nyaraló Naptár</h1> <h1 class="site-title" style="font-family:'Playfair Display',serif;font-size:var(--title-size);font-weight:700;color:#3A332D;margin:4px 0 8px;">Nyaraló Naptár</h1>
<div class="header-line" style="margin:0 auto 12px;"></div> <div class="header-line" style="margin:0 auto 12px;"></div>
<div style="font-size:12px;color:#8B7E74;margin-bottom:4px;">Ki vagy?</div> <div style="font-size:calc(var(--font-size) - 1px);color:#8B7E74;margin-bottom:4px;">Ki vagy?</div>
<div class="login-members" id="login-members"></div> <div class="login-members" id="login-members"></div>
<input type="password" id="login-password" placeholder="Jelszó" onkeydown="if(event.key==='Enter')doLogin()"> <input type="password" id="login-password" placeholder="Jelszó" onkeydown="if(event.key==='Enter')doLogin()">
<button class="btn-send" onclick="doLogin()">Belépés</button> <button class="btn-send" onclick="doLogin()">Belépés</button>
@@ -180,8 +187,8 @@
<div id="app" class="container" style="display:none;"> <div id="app" class="container" style="display:none;">
<div class="header"> <div class="header">
<div class="header-sub">Révfülöp · Balaton</div> <div class="header-sub site-subtitle">Révfülöp · Balaton</div>
<h1>Nyaraló Naptár</h1> <h1 class="site-title">Nyaraló Naptár</h1>
<div class="header-line"></div> <div class="header-line"></div>
</div> </div>
@@ -271,8 +278,28 @@ async function api(method, path, body) {
return res.json(); return res.json();
} }
// Apply UI config as CSS variables
async function loadConfig() {
try {
const config = await fetch('/api/config').then(r => r.json());
const root = document.documentElement;
root.style.setProperty('--font-size', config.fontSize + 'px');
root.style.setProperty('--title-size', config.titleSize + 'px');
root.style.setProperty('--calendar-size', config.calendarSize + 'px');
root.style.setProperty('--button-size', config.buttonSize + 'px');
// Update site name/subtitle if customized
document.querySelectorAll('.site-title').forEach(el => el.textContent = config.siteName);
document.querySelectorAll('.site-subtitle').forEach(el => el.textContent = config.siteSubtitle);
} catch (e) {
console.log('Using default UI config');
}
}
// Auth // Auth
async function checkAuth() { async function checkAuth() {
// Load UI config first (no auth needed)
await loadConfig();
// Always load members first (available without auth) // Always load members first (available without auth)
members = await fetch('/api/members').then(r => r.json()); members = await fetch('/api/members').then(r => r.json());
buildLoginMembers(); buildLoginMembers();
+17 -1
View File
@@ -11,7 +11,17 @@ const DB_PATH = process.env.DB_PATH || '/data/revfulop.db';
const AUTH_PASSWORD = process.env.SIMPLE_AUTH_PASSWORD || ''; const AUTH_PASSWORD = process.env.SIMPLE_AUTH_PASSWORD || '';
const AUTH_ENABLED = AUTH_PASSWORD.length > 0; const AUTH_ENABLED = AUTH_PASSWORD.length > 0;
// Family members config (can be overridden via FAMILY_MEMBERS env var as JSON) // UI config (all overridable via env vars, no rebuild needed)
const UI_CONFIG = {
fontSize: process.env.UI_FONT_SIZE || '15', // base font size in px
titleSize: process.env.UI_TITLE_SIZE || '32', // main title size in px
calendarSize: process.env.UI_CALENDAR_SIZE || '14', // calendar day number size in px
buttonSize: process.env.UI_BUTTON_SIZE || '14', // button/input font size in px
siteName: process.env.UI_SITE_NAME || 'Nyaraló Naptár',
siteSubtitle: process.env.UI_SITE_SUBTITLE || 'Révfülöp · Balaton',
};
// Family members config (can be overridable via FAMILY_MEMBERS env var as JSON)
const DEFAULT_MEMBERS = [ const DEFAULT_MEMBERS = [
{ id: "katinka", name: "Katinka", color: "#513eff" }, { id: "katinka", name: "Katinka", color: "#513eff" },
{ id: "orsi", name: "Orsi", color: "#a15dd8" }, { id: "orsi", name: "Orsi", color: "#a15dd8" },
@@ -122,6 +132,11 @@ app.get('/api/members', (req, res) => {
res.json(FAMILY_MEMBERS); res.json(FAMILY_MEMBERS);
}); });
// UI config endpoint
app.get('/api/config', (req, res) => {
res.json(UI_CONFIG);
});
// Bookings CRUD // Bookings CRUD
app.get('/api/bookings', (req, res) => { app.get('/api/bookings', (req, res) => {
const bookings = db.prepare('SELECT * FROM bookings ORDER BY start_date').all(); const bookings = db.prepare('SELECT * FROM bookings ORDER BY start_date').all();
@@ -191,4 +206,5 @@ app.listen(PORT, '0.0.0.0', () => {
console.log(`Révfülöp Calendar running on port ${PORT}`); console.log(`Révfülöp Calendar running on port ${PORT}`);
console.log(`Auth: ${AUTH_ENABLED ? 'ENABLED (simple password)' : 'DISABLED (public access)'}`); console.log(`Auth: ${AUTH_ENABLED ? 'ENABLED (simple password)' : 'DISABLED (public access)'}`);
console.log(`Members: ${FAMILY_MEMBERS.map(m => m.name).join(', ')}`); console.log(`Members: ${FAMILY_MEMBERS.map(m => m.name).join(', ')}`);
console.log(`UI: font=${UI_CONFIG.fontSize}px, title=${UI_CONFIG.titleSize}px, calendar=${UI_CONFIG.calendarSize}px`);
}); });