Test
<!doctype html>
<html lang="nl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Modern Timer</title>
<script src="/_sdk/element_sdk.js"></script>
<style>
body {
box-sizing: border-box;
margin: 0;
padding: 0;
height: 100%;
overflow: hidden;
}
html {
height: 100%;
}
* {
box-sizing: border-box;
}
.timer-container {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
padding: 2rem;
position: relative;
}
.timer-title {
font-size: 2rem;
font-weight: 700;
color: white;
margin-bottom: 3rem;
text-align: center;
letter-spacing: -0.02em;
}
.timer-display {
position: relative;
margin-bottom: 3rem;
}
.timer-circle {
width: 280px;
height: 280px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border: 3px solid rgba(255, 255, 255, 0.2);
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
transition: transform 0.3s ease;
}
.timer-circle.pulse {
animation: pulse 1.5s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% {
transform: scale(1);
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
}
50% {
transform: scale(1.05);
box-shadow: 0 25px 70px rgba(0, 0, 0, 0.4);
}
}
.timer-text {
font-size: 4.5rem;
font-weight: 700;
color: white;
letter-spacing: -0.03em;
text-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
}
.timer-controls {
display: flex;
gap: 1rem;
flex-wrap: wrap;
justify-content: center;
}
.control-button {
padding: 1rem 2rem;
font-size: 1.125rem;
font-weight: 600;
border: none;
border-radius: 12px;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
min-width: 120px;
}
.btn-start {
background: white;
color: #667eea;
}
.btn-start:hover {
background: #f0f0f0;
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
}
.btn-pause {
background: rgba(255, 255, 255, 0.2);
color: white;
border: 2px solid white;
}
.btn-pause:hover {
background: rgba(255, 255, 255, 0.3);
transform: translateY(-2px);
}
.btn-reset {
background: rgba(255, 87, 87, 0.9);
color: white;
}
.btn-reset:hover {
background: rgba(255, 87, 87, 1);
transform: translateY(-2px);
}
.completion-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
display: none;
align-items: center;
justify-content: center;
flex-direction: column;
animation: fadeIn 0.5s ease;
z-index: 10;
}
.completion-overlay.show {
display: flex;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.completion-text {
font-size: 3rem;
font-weight: 700;
color: white;
margin-bottom: 2rem;
text-align: center;
animation: scaleIn 0.6s ease;
}
@keyframes scaleIn {
0% {
transform: scale(0.5);
opacity: 0;
}
100% {
transform: scale(1);
opacity: 1;
}
}
.celebration-emoji {
font-size: 5rem;
animation: bounce 0.8s ease infinite;
}
@keyframes bounce {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-20px);
}
}
@media (max-width: 768px) {
.timer-title {
font-size: 1.5rem;
margin-bottom: 2rem;
}
.timer-circle {
width: 220px;
height: 220px;
}
.timer-text {
font-size: 3rem;
}
.control-button {
padding: 0.875rem 1.5rem;
font-size: 1rem;
min-width: 100px;
}
.completion-text {
font-size: 2rem;
padding: 0 1rem;
}
.celebration-emoji {
font-size: 4rem;
}
}
</style>
<style>@view-transition { navigation: auto; }</style>
<script src="/_sdk/data_sdk.js" type="text/javascript"></script>
<script src="https://cdn.tailwindcss.com" type="text/javascript"></script>
</head>
<body>
<main class="timer-container">
<h1 class="timer-title" id="timer-title">Focus Timer</h1>
<div class="timer-display">
<div class="timer-circle" id="timer-circle">
<div class="timer-text" id="timer-text">
15:00
</div>
</div>
</div>
<div class="timer-controls"><button class="control-button btn-start" id="start-btn">Start</button> <button class="control-button btn-pause" id="pause-btn">Pauze</button> <button class="control-button btn-reset" id="reset-btn">Reset</button>
</div>
<div class="completion-overlay" id="completion-overlay">
<div class="celebration-emoji">
🎉
</div>
<div class="completion-text" id="completion-text">
Tijd is om!
</div>
</div>
</main>
<script>
const defaultConfig = {
timer_title: "Focus Timer",
completion_message: "Tijd is om!",
background_color: "#667eea",
accent_color: "#764ba2",
text_color: "#ffffff",
button_color: "#ffffff",
button_text_color: "#667eea",
font_family: "Inter",
font_size: 16
};
let totalSeconds = 15 * 60; // 15 minutes
let remainingSeconds = totalSeconds;
let timerInterval = null;
let isRunning = false;
const timerText = document.getElementById('timer-text');
const timerCircle = document.getElementById('timer-circle');
const startBtn = document.getElementById('start-btn');
const pauseBtn = document.getElementById('pause-btn');
const resetBtn = document.getElementById('reset-btn');
const completionOverlay = document.getElementById('completion-overlay');
const timerTitle = document.getElementById('timer-title');
const completionText = document.getElementById('completion-text');
function formatTime(seconds) {
const mins = Math.floor(seconds / 60);
const secs = seconds % 60;
return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
}
function updateDisplay() {
timerText.textContent = formatTime(remainingSeconds);
}
function startTimer() {
if (isRunning) return;
isRunning = true;
timerCircle.classList.add('pulse');
timerInterval = setInterval(() => {
remainingSeconds--;
updateDisplay();
if (remainingSeconds <= 0) {
stopTimer();
showCompletion();
}
}, 1000);
}
function pauseTimer() {
isRunning = false;
timerCircle.classList.remove('pulse');
clearInterval(timerInterval);
}
function stopTimer() {
pauseTimer();
}
function resetTimer() {
stopTimer();
remainingSeconds = totalSeconds;
updateDisplay();
completionOverlay.classList.remove('show');
}
function showCompletion() {
completionOverlay.classList.add('show');
}
startBtn.addEventListener('click', startTimer);
pauseBtn.addEventListener('click', pauseTimer);
resetBtn.addEventListener('click', resetTimer);
completionOverlay.addEventListener('click', resetTimer);
async function onConfigChange(config) {
const container = document.querySelector('.timer-container');
const customFont = config.font_family || defaultConfig.font_family;
const baseFontStack = '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif';
const baseSize = config.font_size || defaultConfig.font_size;
container.style.background = `linear-gradient(135deg, ${config.background_color || defaultConfig.background_color} 0%, ${config.accent_color || defaultConfig.accent_color} 100%)`;
timerTitle.textContent = config.timer_title || defaultConfig.timer_title;
timerTitle.style.fontFamily = `${customFont}, ${baseFontStack}`;
timerTitle.style.fontSize = `${baseSize * 2}px`;
timerTitle.style.color = config.text_color || defaultConfig.text_color;
timerText.style.fontFamily = `${customFont}, ${baseFontStack}`;
timerText.style.fontSize = `${baseSize * 4.5}px`;
timerText.style.color = config.text_color || defaultConfig.text_color;
completionText.textContent = config.completion_message || defaultConfig.completion_message;
completionText.style.fontFamily = `${customFont}, ${baseFontStack}`;
completionText.style.fontSize = `${baseSize * 3}px`;
startBtn.style.background = config.button_color || defaultConfig.button_color;
startBtn.style.color = config.button_text_color || defaultConfig.button_text_color;
startBtn.style.fontFamily = `${customFont}, ${baseFontStack}`;
startBtn.style.fontSize = `${baseSize * 1.125}px`;
pauseBtn.style.fontFamily = `${customFont}, ${baseFontStack}`;
pauseBtn.style.fontSize = `${baseSize * 1.125}px`;
pauseBtn.style.color = config.text_color || defaultConfig.text_color;
resetBtn.style.fontFamily = `${customFont}, ${baseFontStack}`;
resetBtn.style.fontSize = `${baseSize * 1.125}px`;
}
if (window.elementSdk) {
window.elementSdk.init({
defaultConfig,
onConfigChange,
mapToCapabilities: (config) => ({
recolorables: [
{
get: () => config.background_color || defaultConfig.background_color,
set: (value) => {
config.background_color = value;
window.elementSdk.setConfig({ background_color: value });
}
},
{
get: () => config.accent_color || defaultConfig.accent_color,
set: (value) => {
config.accent_color = value;
window.elementSdk.setConfig({ accent_color: value });
}
},
{
get: () => config.text_color || defaultConfig.text_color,
set: (value) => {
config.text_color = value;
window.elementSdk.setConfig({ text_color: value });
}
},
{
get: () => config.button_color || defaultConfig.button_color,
set: (value) => {
config.button_color = value;
window.elementSdk.setConfig({ button_color: value });
}
},
{
get: () => config.button_text_color || defaultConfig.button_text_color,
set: (value) => {
config.button_text_color = value;
window.elementSdk.setConfig({ button_text_color: value });
}
}
],
borderables: [],
fontEditable: {
get: () => config.font_family || defaultConfig.font_family,
set: (value) => {
config.font_family = value;
window.elementSdk.setConfig({ font_family: value });
}
},
fontSizeable: {
get: () => config.font_size || defaultConfig.font_size,
set: (value) => {
config.font_size = value;
window.elementSdk.setConfig({ font_size: value });
}
}
}),
mapToEditPanelValues: (config) => new Map([
["timer_title", config.timer_title || defaultConfig.timer_title],
["completion_message", config.completion_message || defaultConfig.completion_message]
])
});
}
updateDisplay();
</script>
<script>(function(){function c(){var b=a.contentDocument||a.contentWindow.document;if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'9a06844cc3b213d7',t:'MTc2MzQ1ODkzNi4wMDAwMDA='};var a=document.createElement('script');a.nonce='';a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body>
</html>