Skip to content

Commit 027f7a0

Browse files
authored
Merge pull request #3340 from AtCoder-NoviSteps/#3338
fix: Suppress spinner on same-route navigation in layout (#3338)
2 parents 4499569 + 041b2fa commit 027f7a0

3 files changed

Lines changed: 32 additions & 5 deletions

File tree

.claude/rules/svelte-components.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,20 @@ Import from `flowbite-svelte`. Use Tailwind CSS v4 utility classes. Dark mode: `
3737

3838
When copying button styles from a reference component, always check all three axes: `color`, `size`, and `class`. Omitting `color` applies Flowbite's default (filled blue).
3939

40+
## Complex `{#if}` Conditions — Extract to Named Functions
41+
42+
When a template condition requires API-specific knowledge or combines multiple null checks, extract it to a private function in `<script>`. A named function communicates intent at the call site; the implementation detail stays in one place. This applies even when the function is used only once.
43+
44+
```svelte
45+
<!-- Bad: requires knowing $app/state's null semantics to understand -->
46+
{#if navigating.from !== null && navigating.from.route.id !== navigating.to?.route.id}
47+
48+
<!-- Good: intent readable without knowing the API -->
49+
function isCrossRouteNavigation(): boolean { ... }
50+
51+
{#if isCrossRouteNavigation()}
52+
```
53+
4054
## `{@const}` Placement
4155

4256
`{@const}` must be an **immediate child** of a block statement (`{#if}`, `{#each}`, `{:else}`, `{#snippet}`, etc.). Placing it inside an HTML element is a compile error:

.claude/rules/sveltekit.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ const grade = gradeRaw as TaskGrade;
7373

7474
The same pattern applies to `url.searchParams.get()` in `+server.ts` handlers.
7575

76+
## `$app/state`: navigating Idle Check
77+
78+
`navigating` from `$app/state` always exists as an object. Use `navigating.from === null` to detect the idle state — not `navigating === null`.
79+
80+
To limit spinner display to cross-route navigation only, compare `navigating.from.route.id` with `navigating.to?.route.id`. Same-route query-param changes produce equal ids.
81+
7682
## Page Component Props
7783

7884
SvelteKit page components (`+page.svelte`) accept only `data` and `form` as props (`svelte/valid-prop-names-in-kit-pages`). Commented-out features that reference other props are not "dead code" — remove only the violating prop declaration, preserve the feature code.

src/routes/+layout.svelte

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22
// See:
33
// https://github.com/oekazuma/svelte-meta-tags
44
// https://oekazuma.github.io/svelte-meta-tags/ja/migration-guide/
5-
import { page } from '$app/state';
6-
import { navigating } from '$app/stores';
5+
import { navigating, page } from '$app/state';
76
87
import { MetaTags, deepMerge } from 'svelte-meta-tags';
98
@@ -20,6 +19,16 @@
2019
let { data, children } = $props();
2120
2221
let metaTags = $derived(deepMerge(data.baseMetaTags, page.data.pageMetaTags));
22+
23+
// $app/state's navigating has from === null when no navigation is occurring (unlike $app/stores
24+
// where the entire object is null). route.id is a route path pattern (e.g. "/workbooks"),
25+
// so same-route param changes produce equal ids and do not trigger the spinner.
26+
//
27+
// See:
28+
// https://svelte.dev/docs/kit/$app-state#navigating
29+
function isCrossRouteNavigation(): boolean {
30+
return navigating.from !== null && navigating.from.route.id !== navigating.to?.route.id;
31+
}
2332
</script>
2433

2534
<Header />
@@ -29,9 +38,7 @@
2938

3039
<ErrorMessageToast errorMessage={$errorMessageStore} />
3140

32-
<!-- See: -->
33-
<!-- https://svelte.dev/docs/kit/$app-stores#navigating -->
34-
{#if $navigating}
41+
{#if isCrossRouteNavigation()}
3542
<SpinnerWrapper />
3643
{:else}
3744
{@render children?.()}

0 commit comments

Comments
 (0)