185 lines
6.6 KiB
HTML
185 lines
6.6 KiB
HTML
<style>
|
|
.theme-root {
|
|
--bg: #1a1a3e;
|
|
--surface: #12122a;
|
|
--surface2: #1e1e40;
|
|
--border: #2e2e5e;
|
|
--border2: #3a3a6e;
|
|
--text: #e8e8ff;
|
|
--muted: #9090c0;
|
|
--dim: #5050a0;
|
|
}
|
|
.theme-root.day {
|
|
--bg: #f7f6f3;
|
|
--surface: #ffffff;
|
|
--surface2: #f0eeea;
|
|
--border: #e2ddd6;
|
|
--border2: #ccc7be;
|
|
--text: #1a1917;
|
|
--muted: #6b6560;
|
|
--dim: #b0aa9f;
|
|
}
|
|
.scene {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 2rem 1rem;
|
|
gap: 1rem;
|
|
font-family: var(--font-sans);
|
|
background: var(--bg);
|
|
border-radius: var(--border-radius-lg);
|
|
transition: background 0.4s ease;
|
|
}
|
|
.demo-card {
|
|
background: var(--surface);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--border-radius-lg);
|
|
padding: 1rem 1.5rem;
|
|
width: 100%;
|
|
max-width: 280px;
|
|
transition: background 0.4s ease, border-color 0.4s ease;
|
|
}
|
|
.demo-card p { margin: 0 0 4px; font-size: 14px; color: var(--text); transition: color 0.4s ease; }
|
|
.demo-card span { font-size: 12px; color: var(--muted); transition: color 0.4s ease; }
|
|
.demo-tag {
|
|
display: inline-block;
|
|
margin-top: 8px;
|
|
font-size: 11px;
|
|
padding: 2px 8px;
|
|
border-radius: 20px;
|
|
background: var(--surface2);
|
|
color: var(--dim);
|
|
border: 1px solid var(--border2);
|
|
transition: background 0.4s ease, color 0.4s ease, border-color 0.4s ease;
|
|
}
|
|
.toggle-wrap {
|
|
position: relative;
|
|
width: 50px;
|
|
height: 25px;
|
|
cursor: pointer;
|
|
-webkit-tap-highlight-color: transparent;
|
|
}
|
|
.track {
|
|
position: absolute;
|
|
inset: 0;
|
|
border-radius: 12.5px;
|
|
transition: background 0.5s ease;
|
|
overflow: hidden;
|
|
}
|
|
.track.day { background: #87CEEB; }
|
|
.track.night { background: #1a1a3e; }
|
|
.stars { position: absolute; inset: 0; opacity: 0; transition: opacity 0.4s ease; }
|
|
.night .stars { opacity: 1; }
|
|
.star { position: absolute; background: white; border-radius: 50%; animation: twinkle 2s infinite; }
|
|
.star:nth-child(1) { width:2px; height:2px; top:4px; left:6px; animation-delay:0s; }
|
|
.star:nth-child(2) { width:1px; height:1px; top:10px; left:14px; animation-delay:0.4s; }
|
|
.star:nth-child(3) { width:1px; height:1px; top:5px; left:22px; animation-delay:0.8s; }
|
|
.star:nth-child(4) { width:2px; height:2px; top:14px; left:10px; animation-delay:1.2s; }
|
|
.star:nth-child(5) { width:1px; height:1px; top:8px; left:30px; animation-delay:0.2s; }
|
|
@keyframes twinkle { 0%,100% { opacity:1; } 50% { opacity:0.3; } }
|
|
.knob {
|
|
position: absolute;
|
|
top: 3px;
|
|
width: 19px;
|
|
height: 19px;
|
|
border-radius: 50%;
|
|
transition: left 0.45s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.4s ease;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
z-index: 2;
|
|
}
|
|
.knob.night {
|
|
left: 3px;
|
|
background: radial-gradient(circle at 35% 35%, #e8e8ff, #c8c8f8);
|
|
box-shadow: 0 0 0 2px rgba(180,180,255,0.18), 0 0 7px 4px rgba(140,140,255,0.35), 0 0 14px 6px rgba(100,100,220,0.18);
|
|
}
|
|
.knob.day {
|
|
left: 28px;
|
|
background: radial-gradient(circle at 40% 40%, #fff8c5, #FFD700);
|
|
box-shadow: 0 0 0 3px rgba(255,220,0,0.25), 0 0 8px 4px rgba(255,200,0,0.45), 0 0 16px 6px rgba(255,160,0,0.2);
|
|
}
|
|
.sun-rays { position: absolute; inset: -7px; opacity: 0; transition: opacity 0.3s ease 0.15s; pointer-events: none; }
|
|
.knob.day .sun-rays { opacity: 1; }
|
|
.ray { position: absolute; width: 1.5px; height: 4px; background: rgba(255,220,0,0.9); border-radius: 1px; left: 50%; transform-origin: 50% 16px; margin-left: -0.75px; }
|
|
.moon-crater { position: absolute; border-radius: 50%; background: rgba(140,140,200,0.4); transition: opacity 0.3s ease; }
|
|
.knob.day .moon-crater { opacity: 0; }
|
|
.knob.night .moon-crater { opacity: 1; }
|
|
.label { font-size: 13px; font-weight: 500; user-select: none; color: var(--muted); transition: color 0.4s ease; }
|
|
.state-label { font-size: 11px; color: var(--muted); letter-spacing: 0.05em; text-transform: uppercase; transition: color 0.4s ease; }
|
|
.row { display: flex; align-items: center; gap: 12px; }
|
|
</style>
|
|
|
|
<div class="theme-root day" id="themeRoot">
|
|
<div class="scene">
|
|
<h2 class="sr-only">Day and night toggle with scoped theme tokens</h2>
|
|
|
|
<div class="demo-card">
|
|
<p>Sample card</p>
|
|
<span>Tokens update on toggle</span><br>
|
|
<span class="demo-tag">surface2 · dim · border2</span>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<span class="label">Night</span>
|
|
<div class="toggle-wrap" role="switch" aria-checked="true" tabindex="0" id="toggle" aria-label="Toggle day/night mode">
|
|
<div class="track day" id="track">
|
|
<div class="stars">
|
|
<div class="star"></div><div class="star"></div><div class="star"></div>
|
|
<div class="star"></div><div class="star"></div>
|
|
</div>
|
|
</div>
|
|
<div class="knob day" id="knob">
|
|
<div class="sun-rays" id="rays"></div>
|
|
<div class="moon-crater" style="width:4px;height:4px;top:3px;left:5px;"></div>
|
|
<div class="moon-crater" style="width:3px;height:3px;top:9px;left:9px;"></div>
|
|
<div class="moon-crater" style="width:2px;height:2px;top:4px;left:11px;"></div>
|
|
</div>
|
|
</div>
|
|
<span class="label">Day</span>
|
|
</div>
|
|
|
|
<span class="state-label" id="stateLabel">Day mode</span>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const toggle = document.getElementById('toggle');
|
|
const track = document.getElementById('track');
|
|
const knob = document.getElementById('knob');
|
|
const rays = document.getElementById('rays');
|
|
const stateLabel = document.getElementById('stateLabel');
|
|
const themeRoot = document.getElementById('themeRoot');
|
|
|
|
let isDay = true;
|
|
|
|
for (let i = 0; i < 8; i++) {
|
|
const r = document.createElement('div');
|
|
r.className = 'ray';
|
|
r.style.transform = `rotate(${i * 45}deg)`;
|
|
rays.appendChild(r);
|
|
}
|
|
|
|
function applyState() {
|
|
if (isDay) {
|
|
track.className = 'track day';
|
|
knob.className = 'knob day';
|
|
stateLabel.textContent = 'Day mode';
|
|
toggle.setAttribute('aria-checked', 'true');
|
|
themeRoot.classList.add('day');
|
|
} else {
|
|
track.className = 'track night';
|
|
knob.className = 'knob night';
|
|
stateLabel.textContent = 'Night mode';
|
|
toggle.setAttribute('aria-checked', 'false');
|
|
themeRoot.classList.remove('day');
|
|
}
|
|
}
|
|
|
|
toggle.addEventListener('click', () => { isDay = !isDay; applyState(); });
|
|
toggle.addEventListener('keydown', e => {
|
|
if (e.key === ' ' || e.key === 'Enter') { e.preventDefault(); isDay = !isDay; applyState(); }
|
|
});
|
|
</script>
|