aboutsummaryrefslogtreecommitdiff
path: root/src/routes
Commit message (Collapse)AuthorAgeFilesLines
* feat(a11y): rename Disable Page Transitions to Reduce MotionHEADmainFuwn13 hours1-4/+13
| | | | | | | | | | | | | | | | | | | | | The setting previously only gated the fly slide in Root.svelte. Renaming the label to "Reduce motion" sets the expectation that it acts as a force-on for the browser's prefers-reduced-motion: reduce across the board, so the setting's scope is broadened to match. Storage key (displayDisableAnimations) stays the same so existing user preferences carry over. Only the i18n label and behavior scope change. Adds a reducesMotion() helper that combines the media query with the setting. View Transitions, Lenis init, and the details JS animation now all bypass via this helper. A reactive $: toggles html.reduce-motion on the root element based on the setting; the normalise.css bandaid that already collapsed animations under prefers-reduced-motion is duplicated under that class so every CSS animation and transition (Svelte fly, dropdown panels, hamburger morph, header scroll-hide, the global :where transitions) snaps to 0.01ms when the setting is on.
* fix(nav): respect displayDisableAnimations in view transitionsFuwn13 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 excludedFuwn13 hours1-0/+30
| | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* feat(details): animate details open/close via Web Animations APIFuwn13 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 scrollFuwn14 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(transitions): restore background-color on global anchor transitionFuwn14 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(breakpoints): consolidate 9 ad-hoc values into 5 SCSS tokensFuwn14 hours5-12/+22
| | | | | | | | | | | | | | | | 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 surfacesFuwn14 hours1-3/+4
| | | | | | | | | | | | | | | | | 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 transitionsFuwn14 hours1-7/+7
| | | | | | | | | | | | | 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 hamburgerFuwn15 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 clickFuwn15 hours1-1/+6
| | | | | | | | | | | | | | 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.
* feat(nav): float header as a corner hamburger under 800pxFuwn15 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): scope focus-visible ring to header itemsFuwn3 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
|
* refactor(colours): tokenise card shadows and scrimsFuwn4 days1-2/+2
| | | | | | | | | | | 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 days1-1/+1
| | | | | | | | | | | 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.
* feat(a11y): respect prefers-reduced-motionFuwn4 days1-2/+4
| | | | | | | 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.
* refactor(motion): introduce easing/duration tokensFuwn11 days1-4/+4
| | | | 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-quartFuwn11 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/opacityFuwn11 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.
* fix(scroll): route LandingHero scroll-down through LenisFuwn11 days1-0/+5
| | | | 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 LenisFuwn11 days1-0/+7
|
* feat(command-palette): add quick toggles, sync, and auth actionsFuwn2026-04-261-1/+12
| | | | | | | | 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.
* Revert "fix(api): drop unused redirect query param from oauth refresh"Fuwn2026-04-181-1/+3
| | | | This reverts commit 13226aaeb7c4dc1ce01074ef1ba1eeb87b53d5f5.
* fix(api): await setShadowHidden in badges PUTFuwn2026-04-181-1/+4
| | | | | | | setShadowHidden is async and hits Supabase. The PUT handler called it without await, so the handler could respond before the database write landed (and any error was silently lost). Add the missing await so the response only goes out after the update settles.
* fix(api): drop unused redirect query param from oauth refreshFuwn2026-04-181-3/+1
| | | | | | | | | | | | The refresh endpoint accepted a ?redirect query param and, when present, called redirect(303, "/") instead of returning the refreshed token as JSON. The target was hardcoded to "/" regardless of the param's value, so the feature was dead — and the pattern of reading a "redirect" param invited future open-redirect bugs if someone wired the value through to redirect() directly. The sole in-tree caller (feeds/activity-notifications) reads the JSON response, so always return JSON and drop the redirect import.
* fix(api): gate badge click-count on Origin and fix 401 response reuseFuwn2026-04-181-9/+11
| | | | | | | | | | | | | The PUT ?incrementClickCount path ran before any auth guard, letting unauthenticated callers spam-increment arbitrary badges. Require the request Origin to match appOrigin() so legitimate in-browser clicks (authenticated or not) still count while direct scripted calls are rejected. Also convert the shared `unauthorised` Response singleton into a factory. The singleton's body was consumed on first use, so subsequent 401 paths returned a `Response body is locked` error instead of the intended "Unauthorised" body.
* fix(api): encode subsplease timezone to prevent query-param injectionFuwn2026-04-181-5/+6
| | | | | | | The `tz` query value was interpolated raw into the upstream URL, letting callers append arbitrary query segments (e.g. `tz=foo&f=hax`). Wrap the value in encodeURIComponent and rename the local variable away from the banned `tz` abbreviation.
* fix(ui): balance homepage media panelsFuwn2026-04-121-62/+220
|
* revert(ui): remove april fools executive modeFuwn2026-04-023-99/+22
|
* fix(ui): tune april fools notification copyFuwn2026-04-011-1/+1
|
* fix(ui): simplify april fools controlsFuwn2026-04-011-15/+52
|
* feat(ui): add april fools executive modeFuwn2026-04-013-22/+62
|
* fix(badges): hide outbound link noticeFuwn2026-04-011-1/+1
|
* fix(state): restore persisted list UI stateFuwn2026-03-282-4/+6
|
* fix(cache): preserve hydrated client stateFuwn2026-03-281-1/+2
|
* fix(auth): ignore malformed user cookiesFuwn2026-03-286-24/+54
|
* fix(api): keep preferences publicly readableFuwn2026-03-271-4/+1
|
* fix(profile): restore owner controls for new accountsFuwn2026-03-272-21/+41
|
* refactor(supabase): move app access to service roleFuwn2026-03-272-40/+56
|
* fix(actions): resolve quality and trigger deploy driftFuwn2026-03-221-6/+8
|
* perf: lazy-load authenticated list surfacesFuwn2026-03-222-25/+145
|
* refactor: centralise site origin usageFuwn2026-03-2211-61/+44
|
* refactor(effect): add request body schema decoders to api routesFuwn2026-03-034-5/+36
|
* refactor(effect): migrate api auth cookie decodingFuwn2026-03-034-57/+18
|
* refactor(effect): migrate core auth decode boundariesFuwn2026-03-032-18/+6
|
* chore(cleanup): remove stale eslint directivesFuwn2026-03-012-3/+0
|
* chore(biome): drop formatter style overridesFuwn2026-03-0153-1463/+1597
|
* chore(biome): re-enable noImplicitAnyLet ruleFuwn2026-03-0111-11/+22
|
* chore(biome): enable svelte lintingFuwn2026-03-012-3/+3
|