2026-01-26 11:30:40 -05:00

4.6 KiB

Claude Code Guidelines

Project Overview

This is a SvelteKit 2.x application using Svelte 5 with Tailwind CSS v4.

Code Style Rules

SVG Attributes

When using SVG elements, use style attribute instead of fill attribute to avoid IDE obsolete attribute warnings:

<!-- Correct -->
<svg style="fill: currentColor" ...>
<svg style="fill: none" ...>

<!-- Incorrect (triggers obsolete attribute warning) -->
<svg fill="currentColor" ...>
<svg fill="none" ...>

Svelte 5 Runes

  • Use $state() for reactive state
  • Use $derived() for computed values
  • Use $props() for component props
  • Use $effect() for side effects

Page Store

Use $app/state instead of $app/stores for the page object:

<!-- Correct -->
import {page} from '$app/state'; const path = page.url.pathname;

<!-- Incorrect -->
import {page} from '$app/stores'; const path = $page.url.pathname;

Theme Classes

Use the custom theme utility classes defined in layout.css:

  • text-theme, text-theme-secondary, text-theme-muted
  • bg-theme, bg-theme-card
  • border-theme
  • shadow-theme, shadow-theme-lg

Hover/Active States

Use consistent hover/active patterns:

hover:bg-black/5 dark:hover:bg-white/10
active:bg-black/10 dark:active:bg-white/15

Mobile Grid Layouts

For card grids that should display single-column on mobile and multi-column on larger screens, always:

  1. Explicitly set grid-cols-1 (don't rely on CSS grid's implicit single column)
  2. Add min-w-0 on grid items with flex content to allow proper shrinking
<!-- Correct -->
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
	<div class="card-example flex min-w-0 items-center justify-between">
		<div class="min-w-0 flex-1">...</div>
	</div>
</div>

<!-- Incorrect (may overflow on mobile) -->
<div class="grid gap-4 sm:grid-cols-2">
	<div class="card-example flex items-center justify-between">...</div>
</div>

Button Element Content

Button elements can only contain phrasing content (inline elements). Use <span> instead of <div>, <p>, or <h1>-<h6> inside buttons:

<!-- Correct -->
<button>
	<span class="flex items-center gap-2">
		<span>Click me</span>
	</span>
</button>

<!-- Incorrect (triggers HTML validation warning) -->
<button>
	<div class="flex items-center gap-2">
		<p>Click me</p>
	</div>
</button>

For multi-line button content, use <span class="block"> to create line breaks:

<button>
	<span class="flex items-center gap-2">Title</span>
	<span class="text-muted block text-sm">Subtitle on new line</span>
</button>

SvelteSet Reactivity

SvelteSet from svelte/reactivity is already reactive and does NOT need to be wrapped in $state():

<!-- Correct -->
import { SvelteSet } from 'svelte/reactivity';
let expandedItems = new SvelteSet<string>();

<!-- Incorrect (unnecessary wrapper) -->
let expandedItems = $state(new SvelteSet<string>());

Each Block Keys

Always provide a key for {#each} blocks to avoid ESLint warnings:

<!-- Correct -->
{#each items as item (item.id)}
{#each days as day (day)}

<!-- Incorrect -->
{#each items as item}

Date Formatting

Use the date utilities from $lib/utils/date which use date-fns to properly handle ISO date strings without timezone offset issues:

import {formatDate} from '$lib/utils/date';

<!-- Returns empty string for null/undefined, so add fallback -->
{formatDate(startDate) || '—'}

File Organization

  • Place .svelte.ts files (stores, shared state) in src/lib/stores/, not in component folders
  • GraphQL mutations go in src/lib/graphql/mutations/{entity}/
  • GraphQL queries go in src/lib/graphql/queries/{entity}/

Backend Integration Notes

  • The backend uses a unique constraint allowing only ONE active scope per address
  • The isConditional field on tasks is legacy and should always be set to false
  • GraphQL inputs use camelCase (e.g., isActive, accountId) which maps to snake_case in Django

Task Frequency Values

The backend TaskFrequencyChoices enum uses lowercase values. Handle frequency differently depending on the operation:

Individual mutations (create/update task or task template) - use lowercase:

frequency: 'daily' | 'weekly' | 'monthly' | 'quarterly' | 'triannual' | 'annual' | 'as_needed';

JSON import (createScopeTemplateFromJson) - uppercase is acceptable:

{ "frequency": "DAILY" }

The backend's build_scope_template service normalizes uppercase to lowercase automatically.

Valid frequency values: daily, weekly, monthly, quarterly, triannual, annual, as_needed