Tick Tock
- 33 Devlogs
- 50 Total hours
Tools about Time
Tools about Time
.timeline-track) with flex-proportional work/break segments and a dynamic progress pin (.timeline-pin).workDuration, breakDuration, timeRemaining) to ensure clean state initialization.Height = 140 * Count + 180)..export-canvas to ensure optimal vertical spacing regardless of selected cards count.feature/export-image branch into main after verifying clean template rendering and correct node referencing..export-canvas)..progress-card elements into an isolated container (.export-cards-holder), ensuring the on-screen UI remains completely untouched and non-flickering during image generation.ReferenceError caused by leftover references to targetContainer and stampEl inside initExportImageFeature.html2canvas target from main dashboard container to the temporary canvasEl.try...catch block to guarantee off-screen elements are properly garbage collected.html2canvas library to enable one-click PNG image export of the active progress dashboard.--bg-app).<template> element (#export-watermark-template), cleanly separating DOM structure from JavaScript execution logic.now.toLocaleString() to inject real-time snapshot dates and times into exported images.ignoreElements filtering in html2canvas to prevent unchecked/hidden .progress-card elements from creating top/bottom visual gaps in exported images.TypeError on insertBefore/appendChild by referencing clone.firstElementChild to extract the correct Element Node from DocumentFragment.export-watermark-template) and method call mismatches to ensure reliable script execution.#year,month,...) and localStorage synchronization into time-progress.js to preserve users’ toggle preferences across reloads and shares.hashchange event listener to ensure UI check states dynamically update when users use browser navigation (Back/Forward).#share-menu-btn event listener into BaseLayout.astro, eliminating duplicate share logic across individual tool pages.document.title dynamically to provide clean, contextual titles for native Web Share API and clipboard copy fallbacks.#head, #header-content, #custom-menu, and #main-content).--accent-color, --component-bg-standard, --text-heading), ensuring native support for Dark and Light mode toggling.initSidebarToggles, calculateTimeProgress, updateTicksUI, updateProgressCardUI, and updateSingleCardUI).function keywords, JS Automatic Semicolon Insertion (ASI) line-break issues on return statements, and uncalled Date method parentheses.title-label (“TickTock”) out of individual page templates and integrated it directly into the global BaseLayout.astro.<a> tag. Now, clicking the brand logo on any sub-tool instantly routes the user back to the home dashboard Hub, offering an intuitive, web-app-like navigation experience.requestFullscreen() on the individual #clock-screen container caused the browser to isolate it from the DOM layout. This triggered default browser styling which rendered a pitch-black backdrop, and completely hid crucial layout parts like the header, footer, and side menu buttons.document.documentElement (the global <html> node). This aligns the button’s behavior perfectly with standard keyboard F11 fullscreen events.clock.js to digital-clock.js and adjusted the main page path to /src/pages/digital-clock/. This cleanly isolates this feature from upcoming modules like World Clock or Pomodoro Timer./TickTock/ basePath.if(...) conditions inside CSS were breaking colon component constraints.digital-clock.js using the standard document.documentElement.style.setProperty API. The .colon element now seamlessly reads the responsive --clock-colon-width CSS variable, adapting perfectly across Sans, Serif, and Mono configurations without any jumping side effects.#12hr&mono). When you refresh or load the page, your exact preferred theme loads up right away.#date-label placeholder to the template and used the native JavaScript toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }) inside the main runtime loop to render lightweight, perfectly formatted timestamps on every update.requestAnimationFrame loop. By mapping high-precision timestamps through Math.sin(), the opacity gracefully curves between 0.2 and 1.0, producing a perfectly smooth, wave-like breathing animation.#clock-screen element, backed by a global fullscreenchange listener to seamlessly catch manual ESC escapes and keep the toggle button states perfectly in sync.<script is:inline> block to the index page. This bypasses the server-side build process and safely binds the sharing and clipboard logic only when the page loads in the user’s browser.src/pages/index.astro) to serve as the gateway for current and future time tools, using a shared layout for a consistent user experience.README.md to document the new project architecture, local testing steps, and automated GitHub Pages deployment.flex-shrink: 0; to the icon class to force them to keep their correct size.repeat(auto-fit, minmax(250px, 1fr)), which should have allowed multiple columns..home-container) was set to display: flex with align-items: center. In a vertical Flexbox layout, this alignment forces child elements like the grid to shrink to their absolute minimum width based on content, instead of filling the screen.max-width: 300px; was hardcoded onto the .tool-grid. Since two cards plus their gaps required at least 525px to sit side-by-side, the 300px restriction made it mathematically impossible for the browser to create a second column.max-width: 300px; from the grid and replaced it with a generous max-width: 1200px; to allow proper breathing room on large screens.width: 100%; to the grid class, forcing the grid container to break out of the Flexbox compression and stretch completely across the parent container.auto-fit engine to finally see the available width and cleanly arrange the cards into multiple columns.box-sizing: border-box; to the main container so that padding is counted inside the total width.width: 100sww; was written instead of 100svw, and added body { margin: 0; } to clear default browser spaces.The project started as a single page where all HTML, CSS, and JS lived together. When I created the unified BaseLayout.astro, I kept the old global.css. However, that file had specific styles just for the countdown page, which broke the layout of the new homepage.
To fix this, I moved the countdown-specific CSS directly into the <style> tag of the countdown page (index.astro). Astro automatically confines these styles to their own page, ensuring they no longer mess up the homepage layout.
I originally placed the logic script (script.js) inside the src/pages/countdown/ folder to keep it close to the page. During local testing, Astro threw an error saying there was no API route handler for that JavaScript file.
I learned that Astro treats every file inside src/pages/ as a public URL or an API route. To fix this, I moved the file to a new src/scripts/ folder and renamed it countdown.js, keeping the pages folder strictly for actual web pages.
Inside my documentation folder, I named a development note file ship#2.md. When I ran npm run build, the entire build process crashed immediately with a path error.
The build tool (Vite) treats the # symbol as a URL anchor link, which breaks file path reading. I renamed the file to ship2.md, and the system was able to parse the path and build successfully.
This was the biggest challenge. When I imported countdown.js into the page, the build failed multiple times, throwing errors like window is not defined and document is not defined.
Astro runs pages on the server to generate static HTML during the build step. Because Node.js does not have browser features like window or document, the script crashed.
I fixed this with two steps:
?url suffix in Astro so the server only records its path instead of running it.typeof window !== "undefined" check. This ensures the script stays quiet during the server build and only runs once it reaches the user’s browser.index.html) and operational state modules (script.js) into standardized Western European typography strings.README.md) specifying setup logic, reactive routing hooks, and custom persistence implementations.#share-menu-btn) inside the header actions block, utilizing the Web Share API to access native mobile distribution panels.loadInitialState) where an premature execution return statement truncated processing flows, preventing state restorations.initTimer), deploying window.history.replaceState() to inject fully compiled variable string parameters back into empty browser URI paths.clamp() function, anchoring layout metrics dynamically between absolute pixel boundaries and relative viewport height units (vh).#event-label) to smoothly scale inside an adaptive range (clamp(5px, 2vh, 30px)), optimizing structural vertical padding on shifting device orientations.#target-label) using fluid scaling boundaries (clamp(15px, 3vh, 40px)), ensuring consistent geometric clear space relative to adjacent countdown blocks.#target-label) from inline formatting definitions into explicit block layout models (display: block), stabilizing custom spacing calculations and forcing uniform horizontal element boundaries.width: 100%) onto trailing description elements, smoothly pushing metadata strings down beneath the dynamic flexible tracking container.localStorage) to preserve client display configurations across session cycles and application reboots.loadInitialState) to safely aggregate user layout states without stepping over parameter strings delivered via URL fragment references.updateTimer) dedicated to managing strict single-unit tracking parameters.font-variant-numeric: tabular-nums to enforce strict monospaced numeral allocation, keeping external box dimensions static regardless of structural text mutations..mode-single) to adjust component paddings, implement generous baseline tracking, and elevate text sizing bounds when processing simplified metrics.style.css and script.js internal resources.index.html structure down to essential structural markup definitions, using external linkages to boost source reading efficiency and code maintainability.ReferenceError inside the titleInput listener by swapping an un-sanitized token (newDate) with the valid, persistent variable targetString.if(newTitle="")) muted intended comparison logic, replacing it with a strict equality check (newTitle === "").decodeURIComponent() into loadInitialState() and the hashchange observer to prevent percent-encoded character corruption.<meta name="viewport" content="width=device-width, initial-scale=1.0"> into the document structure header.@media (max-width: 600px) breakpoint directives, downscaling internal flex container gaps from 15px to 10px and adjusting modular countdown component nodes for enhanced display symmetry./^\d{4}-\d{2}-\d{2}$/) to sanitize incoming window location string tokens prior to committing them to the execution state.initTimer(). This routine synchronously updates text container parameters, binds default input values, clears residual interval clocks, and re-allocates tracking loops.initTimer() wrapper where repeating the let primitive on the timerId reference allocated a local block-scope variable that collided with pre-initialization clear routines. Removing the redundant declarative keyword reinstated proper execution.initTimer(targetString) to successfully boot up active countdown cycles on fresh page load sequences.<button> containing a <span> with the material-icons class and the text “menu” to render the hamburger icon.background: none, border: none) to remove default browser styling and positioned it at the top-left corner using position: absolute.Hide time boxes that have a value of 0 to keep the UI clean, but prevent a box from hiding if a larger time unit still holds a value (e.g., if years > 0, months must not be hidden even if months == 0).
Implemented a cascading flag logic (let hasValue = false) executed from the largest unit (years) down to the smallest (minutes). Once any larger unit turns the flag true, all subsequent boxes remain visible regardless of their own value. Box elements are hidden dynamically using parentElement.style.display = "none".
The previous countdown relied on dividing total seconds, which failed to calculate years and months accurately because real-world months have a variable number of days (28, 30, 31).
Shifted from pure division to a calendar-based object subtraction method. The system now calculates the differences for Y, M, D, H, M, S individually and applies a borrowing logic from the smallest unit up to the largest. It uses new Date(Y, M, 0).getDate() to dynamically fetch the exact number of days in the previous month when borrowing days.
The correct condition is mo < 0. The variable represents the difference between months. If mo equals 0, the target month and current month are the same, meaning no year needs to be borrowed. Using <= 0 would trigger a false borrow when months are identical, causing the month value to incorrectly jump by +12 and the year to decrease by 1.
Corrected the day borrowing condition from if (mo < 0) to if (d < 0).
Fixed a typo where let m (minutes) was mistakenly subtracting now.getMonth() instead of now.getMinutes().
add some todos and changelog
the first project using html this year
making a countdown timer