aboutsummaryrefslogtreecommitdiff
Commit message (Collapse)AuthorAgeFilesLines
* feat(nav): collapse hamburger menu on scrollFuwn29 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 animationFuwn29 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 transitionFuwn29 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()Fuwn29 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 tokensFuwn29 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 surfacesFuwn30 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 transitionsFuwn30 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 hamburgerFuwn30 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 clickFuwn30 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 heroFuwn30 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"Fuwn30 hours1-30/+0
| | | | This reverts commit b429cf8d3b566a6ec665cd4f2ec34f55c3138179.
* fix(a11y): bump command palette result rows to 44px on touchFuwn30 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 800pxFuwn30 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:coarseFuwn31 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 defaultFuwn5 days1-1/+1
|
* style(colours.css): Run formatterFuwn5 days1-12/+12
|
* refactor(colours): tokenise card shadows and scrimsFuwn5 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 modifierFuwn5 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 dashFuwn5 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.
* fix(tooltip): park measurement node off-screen to stop body resizeFuwn2026-04-192-0/+5
| | | | | | Absolute-positioned tooltips were appended to document.body without initial left/top, so their static-flow position extended body scrollWidth/scrollHeight during layout, briefly enlarging the page on hover.
* refactor(wrapped): simplify redundant new Date(Date.now()) callsFuwn2026-04-181-2/+2
| | | | | | new Date(Date.now()) is identical to new Date() and was called twice back-to-back to seed currentYear and selectedYear to the same value. Call new Date() once for currentYear and reuse it for selectedYear.
* refactor(naming): replace banned ctx abbreviation with full namesFuwn2026-04-182-9/+9
| | | | | | | | | | | | | CLAUDE.md prohibits abbreviations like ctx in identifiers. Rename the Cloudflare Worker ExecutionContext parameter to executionContext in the proxy worker (handleMangaChapterCounts, fetch, scheduled), and alias Trigger.dev's destructured { ctx } to taskContext in the notifications scheduled task. The external property name on Trigger.dev's params object is library-defined and remains ctx on the wire. Verified: proxy worker still boots under local wrangler dev and all routes (OPTIONS, POST /manga/chapter-counts, forwardProxyRequest) still respond identically.
* Revert "fix(api): drop unused redirect query param from oauth refresh"Fuwn2026-04-181-1/+3
| | | | This reverts commit 13226aaeb7c4dc1ce01074ef1ba1eeb87b53d5f5.