Skip to content

Commit

Permalink
Doc, test-modus og nytt tema (#1)
Browse files Browse the repository at this point in the history
* Added test mode, to select themes manually

* Added Sámi theme

* updated doc

* added screenshot in doc

* added screenshot in doc

* removed redundant info in readme

* edited look of saami theam

* Correct month indexing

* Fixed incorrect sami flag. Added higher resolution background image

---------

Co-authored-by: Ole Kristian Hoel <ole.k.hoel@ntnu.no>
  • Loading branch information
on and hoel authored Nov 21, 2025
1 parent 1bd7a43 commit 9db5437
Show file tree
Hide file tree
Showing 6 changed files with 252 additions and 0 deletions.
68 changes: 68 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Webclock

En vakker nettbasert klokkeapplikasjon med analog og digital visning for flere norske byer (Trondheim, Ålesund og Gjøvik). Klokken inkluderer dynamiske væranimasjoner og sesongbaserte temaer.

![screenshot](doc-screenshot.png)

## Temaer

Applikasjonen bytter automatisk mellom temaer basert på dagens dato

## Testmodus

Testmodus lar deg manuelt overstyre den automatiske temavelgeren og teste ulike temaer uten å vente på spesifikke datoer.

![Test mode theme selector](doc-testmode.png)

### Aktivere testmodus

Legg til `?mode=test` i URL-en:

```
http://localhost/index.html?mode=test
```

## Legge til nye temaer

Legg til nye temadefinisjoner i `themes`-objektet:

```javascript
mytheme: {
dateRange: { startMonth: 0, startDay: 1, endMonth: 0, endDay: 31 },
clockFace: 'clock-face-mytheme.png',
clockFaceScale: 'cover',
background: 'planks-mytheme.png',
backgroundSize: 'cover',
textColor: '#ffffff',
centerDotColor: 'radial-gradient(...)',
centerDotBorder: '#888',
handColors: {
hour: 'linear-gradient(..s.)',
minute: 'linear-gradient(...)',
second: 'linear-gradient(...)'
}
}
```

Legge til tema-valg i tema-velgeren (test-modus):

```Html
<div class="theme-selector">
<label for="theme-dropdown">Theme:</label>
<select id="theme-dropdown">
<option value="auto">Auto (Date-based)</option>
<option value="default">Default</option>
<option value="halloween">Halloween</option>
<option value="movember">Movember</option>
<option value="christmas">Christmas</option>
<option value="saami">Sámi</option>
<option value="mytheme">My Theme</option>
</select>
</div>
```

## Takk til

- Værdata levert av [yr.no](https://yr.no) / Meteorologisk institutt
- Måneberegninger basert på astronomiske algoritmer

Binary file added clock-face-saami.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc-screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc-testmode.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
184 changes: 184 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,61 @@
padding: 20px;
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
overflow-x: auto;
}

.theme-selector {
position: fixed;
top: 20px;
right: 20px;
background: rgba(255, 255, 255, 0.15);
backdrop-filter: blur(10px);
padding: 15px 20px;
border-radius: 10px;
box-shadow: 0 4px 16px rgba(31, 38, 135, 0.37);
border: 1px solid rgba(255, 255, 255, 0.18);
z-index: 1000;
display: none; /* Hidden by default */
align-items: center;
gap: 10px;
}

.theme-selector.visible {
display: flex;
}

.theme-selector label {
color: white;
font-weight: bold;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
font-size: 1rem;
}

.theme-selector select {
padding: 8px 12px;
border-radius: 5px;
border: 2px solid rgba(255, 255, 255, 0.3);
background: rgba(255, 255, 255, 0.9);
color: #333;
font-size: 1rem;
cursor: pointer;
transition: all 0.3s ease;
outline: none;
}

.theme-selector select:hover {
background: rgba(255, 255, 255, 1);
border-color: rgba(255, 255, 255, 0.5);
}

.theme-selector select:focus {
border-color: #4CAF50;
box-shadow: 0 0 5px rgba(76, 175, 80, 0.5);
}

.clocks-container {
display: flex;
gap: 40px;
Expand Down Expand Up @@ -288,6 +338,24 @@
}

@media (max-width: 768px) {
.theme-selector {
position: relative;
top: 0;
right: 0;
margin-bottom: 20px;
width: calc(100% - 40px);
justify-content: center;
}

.theme-selector label {
font-size: 0.9rem;
}

.theme-selector select {
font-size: 0.9rem;
padding: 6px 10px;
}

.clocks-container {
flex-direction: column;
gap: 20px;
Expand Down Expand Up @@ -535,6 +603,18 @@
</style>
</head>
<body>
<div class="theme-selector">
<label for="theme-dropdown">Theme:</label>
<select id="theme-dropdown">
<option value="auto">Auto (Date-based)</option>
<option value="default">Default</option>
<option value="halloween">Halloween</option>
<option value="movember">Movember</option>
<option value="christmas">Christmas</option>
<option value="saami">Sámi</option>
</select>
</div>

<div class="clocks-container">
<!-- Trondheim Clock -->
<div class="clock-container">
Expand Down Expand Up @@ -1280,6 +1360,39 @@
second: 'linear-gradient(to bottom, #ffd700, #ffed4e, #ffd700)' // Gold
}
},
saami: {
/*
Sámi theme by on@ntnu.no
*/
dateRange: { startMonth: 1, startDay: 1, endMonth: 1, endDay: 28 }, // Feb 1-28
clockFace: 'clock-face-saami.png',
clockFaceScale: 'cover',
background: 'saami-background_16-9.png',
backgroundSize: 'cover',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center',
backgroundAttachment: 'fixed',
textColor: '#d7ffa6',
centerDotColor: '#d7ffa6',
centerDotBorder: '#FFF',
handColors: {
hour: '#FFF',
minute: '#FFF',
second: '#FF3300'
},
clockContainer: {
background: 'rgba(0, 0, 0, 0.15)',
backdropFilter: 'blur(0px)',
borderRadius: '20px',
border: '1px solid rgba(215, 255, 166, 0.5)',
boxShadow: '0 8px 32px rgba(215, 255, 166, 0.3)'
},
cityNames: {
trondheim: 'Tråante',
alesund: 'Ålesund',
gjovik: 'Gjøvik'
}
},
default: {
clockFace: 'clock-face.png',
clockFaceScale: 'cover',
Expand All @@ -1299,6 +1412,13 @@
};

function getCurrentTheme() {
// Check if user has manually selected a theme
const selectedTheme = localStorage.getItem('selectedTheme');
if (selectedTheme && selectedTheme !== 'auto') {
return themes[selectedTheme] || themes.default;
}

// Otherwise use date-based automatic theme selection
const today = new Date();
const month = today.getMonth(); // 0-11
const day = today.getDate();
Expand Down Expand Up @@ -1362,6 +1482,45 @@
element.style.boxShadow = handGlow;
});

// Apply clock container styling if specified in theme
const defaultContainerStyles = {
background: 'rgba(255, 255, 255, 0.1)',
backdropFilter: 'blur(10px)',
borderRadius: '20px',
padding: '30px',
boxShadow: '0 8px 32px rgba(31, 38, 135, 0.37)',
border: '1px solid rgba(255, 255, 255, 0.18)'
};

const containerStyles = theme.clockContainer || {};
document.querySelectorAll('.clock-container').forEach(element => {
// Apply each style, using theme value if provided, otherwise use default
element.style.background = containerStyles.background || defaultContainerStyles.background;
element.style.backdropFilter = containerStyles.backdropFilter || defaultContainerStyles.backdropFilter;
element.style.borderRadius = containerStyles.borderRadius || defaultContainerStyles.borderRadius;
element.style.padding = containerStyles.padding || defaultContainerStyles.padding;
element.style.boxShadow = containerStyles.boxShadow || defaultContainerStyles.boxShadow;
element.style.border = containerStyles.border || defaultContainerStyles.border;
});

// Apply city name overrides if specified in theme
if (theme.cityNames) {
Object.keys(theme.cityNames).forEach(cityKey => {
const cityNameElement = document.querySelector(`#clock-${cityKey}`).closest('.clock-container').querySelector('.city-name');
if (cityNameElement) {
cityNameElement.textContent = theme.cityNames[cityKey];
}
});
} else {
// Reset to default city names if no override
Object.keys(clocks).forEach(cityKey => {
const cityNameElement = document.querySelector(`#clock-${cityKey}`).closest('.clock-container').querySelector('.city-name');
if (cityNameElement) {
cityNameElement.textContent = clocks[cityKey].cityName.toUpperCase();
}
});
}

// Update CSS for clock face image (pseudo-element)
const existingStyle = document.getElementById('theme-style');
if (existingStyle) {
Expand All @@ -1384,6 +1543,31 @@
// Check for theme change every hour
setInterval(applyTheme, 1000 * 60 * 60);

// Initialize theme dropdown
document.addEventListener('DOMContentLoaded', function() {
const themeDropdown = document.getElementById('theme-dropdown');
const themeSelector = document.querySelector('.theme-selector');

// Check if mode=test is in the URL
const urlParams = new URLSearchParams(window.location.search);
const mode = urlParams.get('mode');

if (mode === 'test') {
themeSelector.classList.add('visible');
}

// Load saved theme preference
const savedTheme = localStorage.getItem('selectedTheme') || 'auto';
themeDropdown.value = savedTheme;

// Handle theme selection
themeDropdown.addEventListener('change', function() {
const selectedTheme = this.value;
localStorage.setItem('selectedTheme', selectedTheme);
applyTheme();
});
});

// Initialize weather effects with default values first
initializeWeather();

Expand Down
Binary file added saami-background_16-9.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 9db5437

Please sign in to comment.