aboutsummaryrefslogtreecommitdiff
Commit message (Collapse)AuthorAgeFilesLines
* fix(nav): respect displayDisableAnimations in view transitionsFuwn21 hours1-1/+2
| | | | | | | | | | | | Root.svelte already gated the fly transition on \$settings.displayDisableAnimations, but the View Transition path added in 7e2495bd ran unconditionally. The setting appeared broken because the slide kept firing via the browser API. Adds the same check to onNavigate via get(settings) (the callback is not in reactive context, so a synchronous store read is the right shape). When the setting is on, both transition paths bypass and navigation snaps as expected.
* feat(nav): direction-aware view transitions, header excludedFuwn21 hours2-0/+52
| | | | | | | | | | | | | | | | | | | | | | | | | | | Wires SvelteKit's onNavigate hook into document.startViewTransition, with a callback that bypasses cleanly when the API is unavailable or the user prefers reduced motion. Direction is computed from navigation.from/to.pathname using the same navigationOrder logic as the existing fly slide (forward through the ordered routes, backward otherwise; entering /user is +1, leaving -1). Sign is written to --vt-direction on :root before the transition fires. ::view-transition-old(root) and -new(root) get explicit slide keyframes that read --vt-direction as a sign multiplier on translateX(200px), replacing the browser default crossfade with a direction-aware page-flip that matches the prior feel. .header carries view-transition-name: app-header so it is pulled out of the root snapshot and treated as a shared element. Since the header lives outside the {#key data.url} block and is the same DOM element on both sides, its morph is a visual no-op: only the body slides past it instead of the whole viewport. Svelte's existing fly transition in Root.svelte still runs hidden beneath the snapshot for browsers without View Transitions support, acting as a graceful fallback.
* perf(load): font-display swap and content-visibility on badge wallFuwn21 hours2-2/+7
| | | | | | | | | | | | | Two unrelated modernisations: - Adds &display=swap to the Roboto and Overpass Google Fonts URLs imported by Wrapped's stylesheet. Avoids the invisible-text flash during font load on the Wrapped page; DM Sans already had this. - Adds content-visibility: auto to each child of .badges (the grid on the badge wall page). Browser skips layout and paint for off -screen badges until they scroll into view. contain-intrinsic-size auto 8rem reserves placeholder space so the scrollbar stays stable.
* feat(details): animate details open/close via Web Animations APIFuwn21 hours1-0/+51
| | | | | | | | | | | | | | | | | | | | | | | | | | A document-level click delegate intercepts <summary> clicks and animates the parent <details> height instead of relying on the browser's instant snap. Initial attempt used the CSS pseudo-element approach (::details-content + interpolate-size: allow-keywords). Both features have shipped at different times across browsers (Safari < 18.2 has neither, Safari 18.2 to 25 only has the pseudo) and degrade in distinct broken ways. JS via WAAPI works in every browser that has shipped Web Animations. Closed height is computed from the summary's offsetHeight plus the details element's vertical padding and border (read from getComputedStyle), so the animation end-state matches the natural collapsed height regardless of per-element padding tweaks (details-unstyled, card variants, etc). Earlier draft animated to summary.offsetHeight only, which undershot by 2 * padding and caused the element to clip text before snapping back to its resting height. Respects prefers-reduced-motion (bypass to native toggle). Uses a WeakMap so rapid toggles cancel the in-flight animation cleanly. Duration 240ms / cubic-bezier(0.22,1,0.36,1) matches the panel-class motion token used elsewhere.
* feat(nav): collapse hamburger menu on scrollFuwn21 hours1-0/+2
| | | | | | | When the menu is open and the user scrolls, they have signalled they want to interact with content rather than the menu. Closes isMenuOpen on any scroll event alongside the existing isHeaderVisible recalculation in the same handler.
* fix(dropdown): scoped transition restores border-radius animationFuwn21 hours1-0/+6
| | | | | | | | | | Dropdown items add border-radius: 8px on hover. The pre-485f1b11 :global(a) transition: all rule used to animate the corner rounding; the narrowed list excluded it. Adds a scoped transition on .dropdown-content a that mirrors the global anchor list plus border-radius, so the corner rounding eases in over 150ms instead of snapping. CommandPalette dropdown sets border-radius in base state so is unaffected.
* fix(transitions): restore background-color on global anchor transitionFuwn21 hours1-0/+1
| | | | | | | | | | | | Commit 485f1b11 narrowed :global(a) transition to color/opacity/ text-decoration-color, intentionally excluding background. That swallowed background-color animation across every anchor, including the dropdown items (Schedule, Profile) whose :hover toggles background-color from base01. Adds background-color to the narrowed list. The original intent of excluding background-image / shorthand background transitions still holds; only background-color was a real loss.
* style(transitions): add .button-badge / .badge-info to :where()Fuwn21 hours1-0/+2
| | | | | | | | | | | | | A surface audit of commit 963d6356 (which narrowed the universal * transition rule) identified two genuine regressions: .button-badge and .badge-info, both <span> chip styles in src/styles/badge.css. Their :hover changes background-color, which the global * rule used to animate but the narrowed :where() list did not catch. Adds them to the :where() list. All other state-change surfaces audited either still match the existing selectors (anchors, buttons, form controls, cards, ARIA roles) or have their own explicit transition rule (covers, tool cards, badge wall, previews).
* style(breakpoints): consolidate 9 ad-hoc values into 5 SCSS tokensFuwn22 hours11-23/+55
| | | | | | | | | | | | | | | | Adds src/styles/_breakpoints.scss with five canonical tokens (\$bp-sm 600, \$bp-md 800, \$bp-lg 1024, \$bp-xl 1280, \$bp-2xl 1600) and migrates all 15 @media (max-width: ...) occurrences across 9 files. Configures vite.config.ts with scss loadPaths so any file can @use "breakpoints" without relative paths. Conservative rounding-up where existing values were near-duplicates: 500 and 512 collapse to 600, 768 collapses to 800, 1000 collapses to 1024. Slightly more viewports get the smaller-layout treatment in those bands, which is usually beneficial on cramped widths. Converts 7 Svelte files from <style> to <style lang="scss"> to access the SCSS tokens. SCSS is a CSS superset, so existing rules stay valid.
* style(nav): add --base0011-strong for grounded glass on nav surfacesFuwn22 hours2-3/+6
| | | | | | | | | | | | | | | | | The existing --base0011 glass tint is #ffffff80 / #0c0c0c80 (50% alpha), which is unusually transparent for floating UI. The desktop header and the mobile hamburger sit over scrolling content, so the content bleeds through and reads as distracting. Introduces --base0011-strong at #ffffffcc / #0c0c0ccc (80% alpha) for floating chrome. The backdrop blur still has enough content to soften, but the surface now reads as grounded rather than barely-present. Applied to: desktop .header background, mobile toggle background and hover, mobile open panel background. Other glass cards (CommandPalette dropdown, Dropdown component content, tooltips, hover covers) keep the softer --base0011 since they sit over relatively static surfaces and the see-through effect there is not distracting.
* style(motion): introduce --duration-base for panel-class transitionsFuwn22 hours2-7/+8
| | | | | | | | | | | | | Adds 0.24s between --duration-fast (0.15s) and --duration-slow (0.4s) for animations that live in the touch-UI floor: panels, sheets, drawers, modals. 150ms reads as crisp on desktop but undershoots the 200-300ms range touch UI conventions prescribe (finger occludes the target during the tap, deliberation pace is slower, and high-DPI displays compress small translateY into less perceptible motion). Applies the new token to the mobile hamburger panel open/close transition and to the bar-morph that fires on the same tap, so the bars and the panel finish arriving together.
* feat(nav): restore scroll-hide and animate mobile hamburgerFuwn22 hours1-8/+24
| | | | | | | | | | | | | | | | | | | | Three changes to the mobile floating header: - Restore the desktop scroll-driven hide/show on the corner button by dropping the transform:none override. Override the translate distance under 800px so the toggle (44px tall at top:1.25rem) and its 5px outline ring fully clear the viewport: translateY(calc(-100% - 2rem)) instead of -150%, which had been leaving 3px of ring poking through. - Replace display:none / display:flex on the open panel (uneanimatable) with always-rendered panel that flips opacity, transform, visibility, and pointer-events. Uses --duration-fast / --ease-out-quart so the motion vocabulary matches the rest of the project. transform-origin is top right so the scale grows from the toggle position. - Move per-item styling out of the .menu-open qualifier so items keep their block layout even while the panel is faded out, avoiding a layout reflow at the moment menu-open flips.
* fix(nav): close dropdowns and hamburger on touch / outside clickFuwn22 hours2-8/+15
| | | | | | | | | | | | | | Two related touch fixes: - Dropdown :hover rule was sticky on touch devices after a tap, keeping the menu visible even when the click toggle set open to false. Gate the hover rule behind @media (hover: hover) so only true pointer devices use the hover path; touch uses the click-driven open class. - Hamburger menu had no outside-click close. Added a window click handler that closes isMenuOpen when the target is outside .header. Clicks on the toggle and on nav items stay inside .header, so opening and item navigation are unaffected.
* fix(hero): swap 100vh for 100dvh on landing heroFuwn22 hours1-1/+1
| | | | | | | 100vh on mobile Safari/Chrome locks to the largest viewport (URL bar collapsed), so the hero overflows by the bar's height when the page loads with the bar visible, then shrinks when the user scrolls. 100dvh tracks the dynamic viewport and avoids the jump.
* Revert "fix(a11y): bump touch targets to 44px under pointer:coarse"Fuwn22 hours1-30/+0
| | | | This reverts commit b429cf8d3b566a6ec665cd4f2ec34f55c3138179.
* fix(a11y): bump command palette result rows to 44px on touchFuwn22 hours1-0/+7
| | | | | | | | Result rows are <a>, not <button>, so the global input.css coarse -pointer height bump did not cascade. Adds a scoped pointer:coarse block that lifts vertical padding from 0.5em to 0.875em (~46px tall at the palette's 1.05em base) and trims row margin so the taller rows do not double-space.
* feat(nav): float header as a corner hamburger under 800pxFuwn22 hours1-2/+130
| | | | | | | | | | | | | | | Below 800px the inline header overflows the viewport. Strips card chrome from .header itself and floats it position:fixed at top:1.25rem/right :1.25rem so it does not consume a horizontal band. The 44x44 toggle button carries the desktop banner's exact card recipe (--base0011 glass, shadow-card-emphasized + 5px --base02 ring, blur, 8px radius), and the open panel mirrors it as a separately-positioned card below. Menu closes on route navigation and Escape. Header stays visible while the menu is open so a scroll-driven hide does not chop the open sheet mid-interaction. Profile-avatar anchor and the bullet separator are hidden in the mobile menu (avatar is redundant alongside the Profile dropdown; separator reads as line noise vertically).
* fix(a11y): bump touch targets to 44px under pointer:coarseFuwn23 hours1-0/+30
| | | | | | | | Inputs, buttons, and selects baseline at 32px (2em), well below WCAG 2.5.5's 44x44 minimum. Adds a coarse-pointer media block that lifts the standard height to 2.75rem, neutralises aggressive 0.75 scale on .smaller-button / .button-square (transforms shrink hit-test rects), and grows the checkbox from 1.15em to 1.5rem.
* fix(a11y): scope focus-visible ring to header itemsFuwn4 days1-0/+10
| | | | | | | | Add explicit .header-item:focus { outline: none } and .header-item:focus-visible rules in +layout.svelte so the navbar matches the dropdown menu's teal rounded ring on keyboard focus and stops leaking the browser default blue square ring on mouse click. Same pattern as the Dropdown component's scoped focus-visible rule.
* ui(settings): collapse the debug panel by defaultFuwn4 days1-1/+1
|
* style(colours.css): Run formatterFuwn4 days1-12/+12
|
* refactor(colours): tokenise card shadows and scrimsFuwn4 days12-32/+26
| | | | | | | | | | | Introduce --shadow-card, --shadow-card-emphasized, --shadow-cta, --scrim, --scrim-soft, and --scrim-banner. Migrate 14 inline literals (card.css, input.css, Notification, MediaRoulette, the two Landing CTA buttons, the popup overlay, the palette and roulette scrims, three identity banners, the user profile cover art shadow) onto the tokens, with light/dark adaptation handled by the existing prefers-color-scheme blocks instead of duplicated inline. Two single-use Landing demo-focus values stay inline.
* style(card): move backdrop blur to an opt-in .card-glass modifierFuwn4 days7-5/+9
| | | | | | | | | | | Strip backdrop-filter: blur(4px) from the base .card so the ~50 in-flow surfaces (landing panels, schedule rows, tool cards, user profile cards, badge wall, etc.) stop paying for a blur they don't need. Introduce .card-glass for the surfaces that actually float over other content: the sticky page header, header nav dropdowns, command palette, title-attribute tooltips, LinkedTooltip, and HoverCover. Popup, MediaRoulette, and Notification keep their existing parent-overlay or solid-background treatments.
* fix(a11y): drop redundant alts and the landing em dashFuwn4 days6-6/+6
| | | | | | | | | Replace generic alt="Avatar" / alt="Character" with empty alts on images that visually duplicate a name already present as adjacent text (Wrapped activity avatar, Hololive stream icon, three Birthdays covers); screen readers stop announcing "Avatar" / "Character" twice. Also swap the em dash in the landing subheadline for a comma per the project copy rule.
* perf(transitions): narrow the universal * selectorFuwn5 days1-1/+13
| | | | | | | | | Replace the * { transition: color/bg/border/shadow } rule with a :where() list of the surfaces that actually receive state changes (anchors, buttons, form controls, cards, details/summary, role=button, role=menuitem, role=option). Every element no longer pays the transition (especially box-shadow, the costly one) on theme switch or parent re-paint.
* perf(images): lazy-load and async-decode off-screen imageryFuwn5 days10-8/+25
| | | | | | | | | Add loading=lazy and decoding=async to the 16 <img> elements that weren't already deferring across Tools/Wrapped, Events, EasterEvent, Hololive, and the rate-limited fallback. Also drop the backdrop-filter: blur(160px) the dropdown items were paying on every hover; the background-color change already gives sufficient feedback, and the parent card's own blur stays.
* fix(a11y): give CommandPalette real dialog and combobox semanticsFuwn5 days1-3/+47
| | | | | | | | Wrap the palette in role=dialog with aria-modal, mark the overlay aria-hidden, and turn the search input into a labeled combobox driving a listbox of role=option results via aria-activedescendant. Trap Tab on the input, preventDefault on Escape, and restore focus to the previously-focused element when the palette closes.
* style(a11y): drop redundant border-radius from Dropdown focus ruleFuwn5 days1-1/+0
| | | | | The scoped :focus-visible rule on dropdown menu items no longer needs its own border-radius now that the global rule sets it.
* style(a11y): round the focus-visible ringFuwn5 days1-0/+1
| | | | | | Add border-radius: 4px to the global :focus-visible rule so the ring looks consistent on bare anchors and header items, not just on elements that already carry their own radius.
* fix(a11y): make header Dropdown keyboard-operableFuwn5 days1-6/+83
| | | | | | | | | | | | Wire Enter/Space/ArrowDown/ArrowUp/Escape on the toggle and ArrowDown/ArrowUp/Home/End/Escape on each item so the Schedule and Profile menus are reachable without a mouse. Add aria-haspopup, aria-expanded, aria-controls, and role=menu/menuitem; give each instance a unique toggle/menu id so the two header dropdowns no longer collide. Close the menu on item activation so preventDefault items (e.g. Log Out) don't leave it hanging open. Focus moves via `await tick()` so :focus-visible matches reliably, with a scoped fallback outline tuned to var(--base0D) for the menu items.
* feat(a11y): respect prefers-reduced-motionFuwn5 days2-2/+15
| | | | | | | Add a global reduced-motion media query that collapses animation and transition durations and disables smooth scroll. Skip Lenis init when the same preference is set so the JS-driven smooth scroll falls back to the browser default, which the spec already honours.
* fix(a11y): restore focus-visible ringFuwn5 days1-1/+6
| | | | | | | Replace `a:focus { outline: none }` with `:focus { outline: none }` + `:focus-visible { outline: 2px solid var(--base0D); outline-offset: 2px }` so keyboard users see focus on every interactive element while mouse clicks stay clean.
* refactor(motion): introduce easing/duration tokensFuwn12 days4-8/+15
| | | | Add --ease-out-quart, --ease-in-out-quart, --duration-fast, --duration-slow in motion.css and migrate the global anchor, header, and theme-switch transitions to use them. Establishes a shared motion vocabulary for future polish.
* style(header): soften hide/show curve with ease-out-quartFuwn12 days1-1/+1
| | | | Replace transform 0.3s ease with 0.4s cubic-bezier(0.22, 1, 0.36, 1) so the header settles instead of snapping, pairing with Lenis-smoothed scroll.
* style(anchor): narrow global anchor transition to color/opacityFuwn12 days1-1/+4
| | | | Replace transition: all with explicit color, opacity, and text-decoration-color so unrelated property changes (transform, background) do not get incidentally tweened.
* style(transitions): lengthen page transition for smoother feelFuwn12 days1-1/+1
| | | | Bump Root fly duration/delay from 100ms to 180ms so the built-in opacity fade is perceptible and the slide reads against Lenis-smoothed scroll.
* fix(scroll): route LandingHero scroll-down through LenisFuwn12 days3-1/+14
| | | | Lenis disables native CSS smooth scroll, so window.scrollTo({behavior:"smooth"}) jumps instantly. Expose the Lenis instance via a store and scroll through it, falling back to native when unavailable.
* feat(scroll): add global smooth scrolling via LenisFuwn12 days3-0/+27
|
* style: apply biome formatterFuwn2026-04-284-9/+14
|
* refactor(airing): unify countdown formatter into shared helperFuwn2026-04-283-121/+67
|
* fix(airing): round residual hour and minute in AiringTime componentFuwn2026-04-281-8/+8
|
* fix(airing): floor hour consistently in hours countdownFuwn2026-04-281-3/+9
|
* fix(airing): round residual hour in days countdownFuwn2026-04-281-2/+7
|
* feat(command-palette): add quick toggles, sync, and auth actionsFuwn2026-04-264-1/+326
| | | | | | | | Adds 13 reactive quick toggles (24h time, animations, blur adult, cover modes, hover cover, schedule list, reverse sort, data saver, notifications, language, title format cycle, outbound link target cycle), three sync actions (push/pull/disable), and login/logout entries gated on auth state. Names reflect current state so the palette doubles as a status surface.
* fix(tooltip): park off-screen on create, enable glide after first placementFuwn2026-04-192-4/+9
| | | | | | | | | | | | | | | The tooltip was appended to body with no top/left, so its static-flow position extended body.scrollWidth/scrollHeight during measurement. Fix by setting initial top/left to -9999px so the node never lands in the flow. Previously this slid the tooltip in from off-screen because the top/left 0.3s transition was already live. Now the transition starts as opacity-only at creation time; after the first updateTooltipPosition snaps into place, a requestAnimationFrame re-enables the top/left easing so subsequent mousemove updates keep the original smooth glide and 100ms debounce behavior. LinkedTooltip's measurement div is parked the same way plus visibility:hidden, since it exists only to read offsetWidth for the Svelte-rendered tooltip.
* Revert "fix(tooltip): use fixed positioning and snappy cursor tracking"Fuwn2026-04-192-12/+15
| | | | This reverts commit 28860bb88da4c08e3ba383adc9c23ae3689310b6.
* fix(tooltip): use fixed positioning and snappy cursor trackingFuwn2026-04-192-15/+12
| | | | | | | | | | | | | Body grew on hover because the absolutely-positioned tooltip was appended to document.body without top/left, so its static-flow position extended body.scrollWidth/scrollHeight during layout measurement. Switch both the use:tooltip directive and LinkedTooltip's measurement div to position:fixed, which is relative to the viewport and does not contribute to the body's scroll area. The directive now also uses clientX/clientY (dropping the scrollY offset for the pin branch), removes the 100ms mousemove debounce, and drops the top/left transition so the tooltip tracks the cursor without feeling laggy or sliding in from the page top.
* Revert "fix(tooltip): park measurement node off-screen to stop body resize"Fuwn2026-04-192-5/+0
| | | | This reverts commit 6865cae76cb88aa78d7c297c637557468fdce8fc.
* Revert "fix(tooltip): drop position transition so tooltip snaps instead of ↵Fuwn2026-04-191-1/+1
| | | | | | sliding" This reverts commit 6e780fdb76155669a72692c64fd51c0da2c932d2.
* fix(tooltip): drop position transition so tooltip snaps instead of slidingFuwn2026-04-191-1/+1
| | | | | | | The opacity+top+left transition combined with the off-screen parking caused tooltips to animate from -9999px into place ("pops down from the top") and lagged cursor tracking in the non-pinned variant used by birthdays. Keep only the opacity fade; position updates snap.