Profile

Architecture

Architecture

BiKL Dugga Elo is a portable static web application. index.html owns the semantic HTML, responsive CSS, frozen media manifest, album controller, and official YouTube IFrame Player API integration. There is no build step, service worker, application backend, API key, or locally hosted music.

Layouts

The first child of the application root is one non-interactive, aria-hidden scenery layer. Its inline SVG definitions are reused by seven logical clouds across near, middle, and far depth bands and by four kash-flower clusters. Each moving cloud has a paired SVG copy one viewport behind it, while a CSS sky gradient and an inline SVG grass horizon cover the viewport beneath every layout. The artwork has a complete static composition: blue sky, a restrained warm glow, scattered cumulus clouds, layered green grass, and ivory kash plumes. The horizon and flowers stay weighted toward the lower and outer edges so the centered title, album, and music controls remain visually primary. The body uses a matching sky color before the layer is painted or if it cannot be rendered.

Normal mode covers portrait phones, tablets in both orientations, and desktop. The viewport is a three-row grid: a transparent title header, flexible album, and a transparent music region. Narrow portrait uses one 4:5 page with a left binding. Wider normal viewports use a centered pair of 4:5 pages with a center binding. Two-page presentations separate the leaves with a slim recessed gutter that exposes the oxblood leather beneath the spiral while the coil hardware covers most of its width. Both sit on distinct left and right panels of a burnished oxblood leather cover with stitched edges and a parchment page-stack layer. Antique-brass closure buttons retain the existing previous/next interfaces at the outer edges in landscape; every portrait layout hides them and relies on tap halves, horizontal swipes, and arrow keys. Wider spreads may grow to approximately 1180 px before decoration. Within the music region, a rounded mini-player floats near the bottom center. It contains the rotating BiKL disc, a sans-serif current-title/progress block, and right-aligned transport controls with a dominant play/pause action. Its slim translucent surface uses blur, a highlighted border, inset depth, and shadow to retain contrast over changing backgrounds. A ResizeObserver activates a measured title marquee only when metadata exceeds the available width; reduced motion leaves the title stationary. Factual playback status is maintained in a screen-reader-only live region instead of a visible label. The independent 200×200 YouTube iframe remains mounted for API playback but its surface is clipped, inert, and unfocusable by default. The startup show-yt-iframe feature flag makes the native surface unobstructed at the bottom right. At widths up to 700 px the mini-player then stacks above that surface and the music row grows to reserve space for both. With the default flag the shorter music row centers the mini-player without reserving iframe space.

Restricted landscape activates only when the media query reports landscape, width at most 900 px, and height at most 500 px. The content row becomes two compact 5:4 pages plus one prominent play/pause button. The leather reveal, stacked edges, and closure hardware become slimmer, but both album navigation targets remain available. The full music footer is clipped and inert while its iframe stays alive. Disc artwork, track information, progress, time, previous/next controls, and the native iframe surface are absent visually.

A ResizeObserver fits the decorated shell to its flexible workspace. Sizing subtracts workspace padding, leather reveal, stacked-page depth, and any rendered closure overhang explicitly, then calculates the paper pages. The two-page ridge is added to the book and shell only after that calculation, so it widens the album without reducing either page or photograph. Hidden portrait closures contribute zero overhang, allowing the pages to use the recovered width; decoration therefore does not reduce the page dimensions. Every nested grid allows its children to shrink with min-width: 0 and min-height: 0; the root uses safe-area padding, dynamic viewport units, and clipped overflow. Layout selection comes only from the restricted media query and the normal workspace width, so ordinary landscape tablets and desktops never enter compact mode.

Album state and loading

The frozen ALBUM_IMAGES array preserves deterministic filename order. Each entry records a 1600 px display derivative, canonical retry URL, reserved display dimensions, and descriptive alt text. Two reusable image slots render the current state. A failed derivative retries the canonical image once and then becomes a stable paper placeholder. Each light-crema leaf has only a narrow edge inset. The rendered photograph is contained without cropping inside a 5–8 px ivory mat (3 px in restricted landscape), selected from its manifest aspect ratio so the mat follows the photograph rather than filling unused leaf space. Temporary turn leaves, blank companions, and error placeholders use the same crema base while the mats remain lighter. Adjacent states are preloaded transiently; there is no hidden 45-image gallery.

The top-level --album-leaf-color and --portrait-closure-display custom properties are the maintainer-facing controls for leaf color and portrait closure visibility.

Album presentation is single, spread, or compact-spread. Single mode steps one photograph; both spread modes step two. Spread anchors are even indices, and the final odd photograph receives an intentional blank companion. Modular index math wraps in both directions.

The page-turn phases are idle, preparing, first-half, midpoint, and second-half. Preparation locks input and preloads the target. A temporary leaf rotates to the midpoint, the canonical pages switch beneath it, and the leaf finishes before it is destroyed. A resize cancels the active Web Animation, commits its selected target, and rebuilds the current presentation. Reduced motion replaces the 480–680 ms three-dimensional turn with a short 160 ms fade and slide.

The five-second slideshow runs only while the document is visible, the album is not held, and animation is idle. Any completed automatic or manual change starts a fresh interval. Pointer capture distinguishes swipes, below-threshold drags, short taps, and long holds. Arrow keys navigate globally except from interactive elements; the shell pointer handler also ignores interactive descendants so a closure click cannot become a second tap-half action. Space controls music only in normal mode. Only manual album changes write to the polite live region.

Player lifecycle

The playlist ID is validated locally before the official IFrame API is loaded. The player is configured as an embedded playlist with native controls, inline playback, looping, no autoplay, and the current HTTP origin. It is created at startup even when the initial viewport is restricted landscape. Custom controls call documented player methods and derive their state from player events. When the feature flag exposes it, the native surface remains the place for volume and mute control. A one-second poll reads only factual time, duration, playlist index, playlist length, and current video title while playback is active and the document is visible. Title state also syncs on ready and player-state transitions; unavailable metadata falls back to Track N of M, then Puja playlist until playlist position is known.

Restricted-layout transitions change only presentation state: they do not pause, snapshot, destroy, clear, or recreate the player. Playback and progress polling continue uninterrupted. The compact and normal play/pause controls share one action and receive the same event-derived icon, disabled state, and accessible label. Returning to normal mode immediately refreshes title, progress, time, and disc state from the existing player.

API timeout or creation failure disables supplemental controls and retains a direct playlist link. Unavailable or embedding-disabled playlist items are recorded and skipped with exhaustion protection. Request/configuration errors are terminal. The disc uses one lifetime CSS animation and toggles only animation-play-state, so pausing preserves its angle; reduced motion suppresses rotation. Disc artwork falls back from the small PNG to the large seal and then to a CSS disc.

Analytics

The Google tag for GA4 property G-89NC1N0W2H loads asynchronously in the document head. Its normal configuration records page and session activity. A guarded trackEvent wrapper sends custom engagement events without making the album or player depend on the analytics library being available.

Album navigation uses separate event names for direction and input method, with context for the source and target image, images per page, presentation, and control surface. Tap-half and leather-closure actions share the click counters; swipe, keyboard, and automatic turns remain distinct. Music controls record previous/next clicks and play/pause requests. song_play is emitted only after the YouTube player confirms playback of a new track occurrence. Track identity, end state, and explicit track navigation prevent buffering or pause/resume from inflating that count while allowing playlist transitions and completed replays to count. Stable labels identify clicks on the four header links. No pointer coordinates or application-defined user identifiers are collected.

Portability and performance

All project asset URLs are relative. The directory therefore works at localhost root and can later move unchanged to a /dugga-elo/ GitHub Pages subpath. The canonical high-resolution photos remain intact, while checked-in progressive web copies cap decode dimensions. The initial photograph is high priority; all other album decodes are current or adjacent. Timers stop in background tabs and page-turn nodes are temporary. The YouTube document remains mounted across responsive layout changes so playback continuity does not depend on reconstruction.

The scenic illustration adds no runtime or raster request. Transform and CSS animation support progressively enables staggered cloud drift in three parallax depth bands and four independent kash sways; all static placement and drawing remain outside the animation. Every cloud lane contains identical primary and trailing copies separated by exactly one viewport. Translating the lane one viewport makes one copy enter from the left as its twin exits right; the end and start frames are visually identical, keeping seven logical clouds continuously on screen without a loop jump. Near lanes complete that path more quickly, while smaller, fainter middle and far lanes move progressively more slowly. Only the lane wrappers receive narrowly scoped will-change, and their animations change only transform. Negative delays distribute clouds across the initial frame without script-driven setup. Reduced-motion preference disables all scenic motion, hides the trailing copies, and retains the primary static positions. The document visibility handler pauses the decorative CSS animations in background tabs and resumes their same timelines on return, alongside the existing slideshow and progress-polling management.