anycoder-e50354b8 / index.html
cyberelf's picture
Upload folder using huggingface_hub
4f5b6ff verified
Raw
History Blame Contribute Delete
10.9 kB
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Responsive Analog Clock</title>
<style>
:root {
--bg: #ffffff;
--ink: #0f172a; /* primary text/face color */
--muted: #64748b; /* minute tick color */
--accent: #ef4444; /* second hand */
--accent-weak: #fca5a5;
--ring: #0f172a;
--shadow: 0 20px 40px rgba(2, 6, 23, 0.12);
--ring-width: clamp(6px, 0.8vmin, 10px);
--center-cap: clamp(8px, 1vmin, 14px);
--major-tick: clamp(4px, 0.6vmin, 6px);
--minor-tick: clamp(1px, 0.25vmin, 2px);
--num-size: clamp(14px, 3.4vmin, 28px);
--brand-size: clamp(11px, 2.6vmin, 16px);
}
* { box-sizing: border-box; }
html, body {
height: 100%;
margin: 0;
background: var(--bg);
color: var(--ink);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji";
}
.wrap {
min-height: 100%;
display: grid;
grid-template-rows: auto 1fr auto;
align-items: center;
justify-items: center;
gap: 1rem;
padding: 2rem 1rem 1.5rem;
}
header {
display: flex;
align-items: baseline;
gap: 1rem;
justify-content: center;
width: 100%;
max-width: 1100px;
}
.title {
font-weight: 700;
letter-spacing: 0.02em;
font-size: clamp(18px, 2.6vmin, 28px);
}
.subtitle {
color: var(--muted);
font-size: clamp(12px, 2.2vmin, 16px);
}
a.brand {
margin-left: auto;
text-decoration: none;
color: var(--ink);
font-size: var(--brand-size);
opacity: 0.8;
border-bottom: 1px solid color-mix(in oklab, var(--ink), transparent 80%);
transition: opacity .2s ease, transform .2s ease;
}
a.brand:hover { opacity: 1; transform: translateY(-1px); }
.digital {
font-variant-numeric: tabular-nums;
letter-spacing: 0.06em;
font-weight: 600;
font-size: clamp(16px, 3.2vmin, 24px);
padding: 0.35rem 0.75rem;
border-radius: 999px;
border: 1px solid color-mix(in oklab, var(--ink), transparent 88%);
background: #fff;
box-shadow: var(--shadow);
user-select: none;
}
.clock {
position: relative;
width: min(86vmin, 540px);
aspect-ratio: 1;
border-radius: 50%;
border: var(--ring-width) solid var(--ring);
background:
radial-gradient(circle at 50% 50%, #fff 0 68%, #f8fafc 69% 100%); /* subtle inner ring */
box-shadow: var(--shadow);
overflow: hidden;
isolation: isolate;
}
/* Minute ring (60 ticks) */
.clock::before {
content: "";
position: absolute;
inset: clamp(10px, 2.2vmin, 18px);
border-radius: 50%;
background:
/* minor ticks (59) */
repeating-conic-gradient(
from -90deg,
color-mix(in oklab, var(--muted), transparent 40%) 0deg,
color-mix(in oklab, var(--muted), transparent 40%) 1deg,
transparent 1deg 6deg
),
/* major ticks (12) */
repeating-conic-gradient(
from -90deg,
var(--muted) 0deg,
var(--muted) 2.2deg,
transparent 2.2deg 30deg
);
-webkit-mask: radial-gradient(circle, #0000 calc(100% - clamp(12px, 1.6vmin, 16px)), #000 calc(100% - clamp(12px, 1.6vmin, 16px)));
mask: radial-gradient(circle, #0000 calc(100% - clamp(12px, 1.6vmin, 16px)), #000 calc(100% - clamp(12px, 1.6vmin, 16px)));
pointer-events: none;
}
/* Numbers */
.numbers {
position: absolute;
inset: 0;
border-radius: 50%;
}
.num {
position: absolute;
left: 50%;
top: 50%;
transform-origin: center center;
font-weight: 700;
font-size: var(--num-size);
user-select: none;
pointer-events: none;
color: var(--ink);
filter: drop-shadow(0 1px 0 rgba(2,6,23,0.06));
transition: transform 0.2s ease;
}
.num small {
font-size: 0.72em;
font-weight: 600;
opacity: 0.85;
}
/* Hands */
.hands { position: absolute; inset: 0; }
.hand {
position: absolute;
left: 50%;
bottom: 50%;
transform: translateX(-50%) rotate(0deg);
transform-origin: 50% 100%;
border-radius: 999px;
will-change: transform;
}
.hand.hour {
width: clamp(7px, 1vmin, 10px);
height: 24%;
background: linear-gradient(#111, #1f2937);
box-shadow: 0 6px 10px rgba(2,6,23,0.15);
}
.hand.minute {
width: clamp(5px, 0.8vmin, 8px);
height: 34%;
background: linear-gradient(#111, #1f2937);
box-shadow: 0 6px 10px rgba(2,6,23,0.15);
}
/* Second hand (animated with CSS) */
.hand.second {
width: clamp(2.5px, 0.55vmin, 4px);
height: 40%;
background: var(--accent);
box-shadow: 0 6px 10px rgba(239, 68, 68, 0.3);
--spin-duration: 60s;
animation: spin linear infinite;
animation-duration: var(--spin-duration);
}
/* Smooth continuous animation, not tied to wall-clock seconds */
@keyframes spin {
from { transform: translateX(-50%) rotate(0deg); }
to { transform: translateX(-50%) rotate(360deg); }
}
/* Optional: pause if user prefers reduced motion */
@media (prefers-reduced-motion: reduce) {
.hand.second { animation: none; }
}
/* Center cap */
.pivot {
position: absolute;
left: 50%;
top: 50%;
width: clamp(16px, 2.4vmin, 22px);
height: clamp(16px, 2.4vmin, 22px);
transform: translate(-50%, -50%);
background: #fff;
border: clamp(3px, 0.5vmin, 4px) solid var(--ink);
border-radius: 50%;
box-shadow: inset 0 1px 1px rgba(2,6,23,0.15), 0 2px 4px rgba(2,6,23,0.12);
z-index: 2;
}
.pivot::after {
content: "";
position: absolute;
left: 50%;
top: 50%;
width: var(--center-cap);
height: var(--center-cap);
transform: translate(-50%, -50%);
background: var(--accent);
border-radius: 50%;
box-shadow: 0 0 0 2px color-mix(in oklab, #fff, transparent 25%);
}
/* Helper: tiny brand at bottom inside the clock */
.brand-inside {
position: absolute;
bottom: clamp(10px, 2.4vmin, 18px);
left: 50%;
transform: translateX(-50%);
font-size: clamp(10px, 2.2vmin, 12px);
color: color-mix(in oklab, var(--ink), transparent 50%);
user-select: none;
pointer-events: none;
}
/* Accessibility: focus outline on digital readout for keyboard users */
.digital:focus-visible {
outline: 2px solid color-mix(in oklab, var(--ink), transparent 60%);
outline-offset: 4px;
}
</style>
</head>
<body>
<div class="wrap">
<header>
<div class="title">Responsive Analog Clock</div>
<div class="subtitle">White background • CSS-animated second hand</div>
<a class="brand" href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" rel="noreferrer noopener">Built with anycoder</a>
</header>
<div class="digital" id="digital" aria-live="polite" title="Current time">
<!-- JS fills HH:MM:SS -->
</div>
<div class="clock" id="clock" aria-label="Analog clock">
<div class="numbers" id="numbers" aria-hidden="true"></div>
<div class="hands" aria-hidden="true">
<div class="hand hour" id="hourHand"></div>
<div class="hand minute" id="minuteHand"></div>
<div class="hand second" id="secondHand"></div>
</div>
<div class="pivot" aria-hidden="true"></div>
<div class="brand-inside">anycoder</div>
</div>
</div>
<script>
(function () {
const els = {
digital: document.getElementById('digital'),
clock: document.getElementById('clock'),
numbers: document.getElementById('numbers'),
hour: document.getElementById('hourHand'),
minute: document.getElementById('minuteHand'),
second: document.getElementById('secondHand')
};
// Create numbers (12 → Roman numerals), 24 → Arabic numerals
function makeNumbers(use24 = false) {
els.numbers.innerHTML = '';
const fragment = document.createDocumentFragment();
for (let i = 1; i <= 12; i++) {
const n = document.createElement('div');
n.className = 'num';
const label = use24 ? (i + 12) : toRoman(i);
n.textContent = label;
n.dataset.h = i; // store hour
fragment.appendChild(n);
}
els.numbers.appendChild(fragment);
layoutNumbers();
}
function toRoman(num) {
const map = [
[1000,'M'], [900,'CM'], [500,'D'], [400,'CD'],
[100,'C'], [90,'XC'], [50,'L'], [40,'XL'],
[10,'X'], [9,'IX'], [5,'V'], [4,'IV'], [1,'I']
];
let out = '';
for (const [v, s] of map) {
while (num >= v) { out += s; num -= v; }
}
return out;
}
function layoutNumbers() {
const radiusPct = 42; // percentage of radius for text baseline
const nums = els.numbers.querySelectorAll('.num');
nums.forEach(n => {
const h = parseInt(n.dataset.h, 10);
const deg = h * 30; // 360/12
n.style.transform = `translate(-50%, -50%) rotate(${deg}deg) translateY(-${radiusPct}%) rotate(${-deg}deg)`;
});
}
// Update hands every frame (hour/minute) to catch time changes smoothly
let lastSec = -1;
function update() {
const now = new Date();
const ms = now.getMilliseconds();
const s = now.getSeconds() + ms / 1000; // smooth fraction
const m = now.getMinutes() + s / 60;
const h = (now.getHours() % 12) + m / 60;
// Hour hand
els.hour.style.transform = `translateX(-50%) rotate(${h * 30}deg)`;
// Minute hand
els.minute.style.transform = `translateX(-50%) rotate(${m * 6}deg)`;
// Digital readout (update only when seconds change to reduce reflow)
const secInt = now.getSeconds();
if (secInt !== lastSec) {
els.digital.textContent = now.toLocaleTimeString([], { hour12: false });
lastSec = secInt;
}
requestAnimationFrame(update);
}
// Responsive: re-layout numbers when size changes
const ro = new ResizeObserver(() => layoutNumbers());
ro.observe(els.clock);
// Initialize
makeNumbers(false);
update();
})();
</script>
</body>
</html>