Skip to content

Commit 18e23f1

Browse files
joewinkeclaude
andcommitted
feature(jat-c6y1q): improve jst template + jat integration package with integrations and better onboarding
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent b110434 commit 18e23f1

File tree

4 files changed

+218
-2
lines changed

4 files changed

+218
-2
lines changed

ide/src/lib/components/CreateProjectDrawer.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2271,10 +2271,10 @@
22712271
</button>
22722272
</div>
22732273
{/if}
2274+
{/if}
22742275
</div>
22752276
{/if}
22762277
</div>
2277-
{/if}
22782278
</div>
22792279

22802280
<!-- Footer Navigation (hidden after success or during creation) -->

ide/src/routes/tasks/+page.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1674,7 +1674,7 @@
16741674
{:else}
16751675
<!-- Voice Inbox: pending suggestions from iOS voice notes (hidden when empty) -->
16761676
<VoiceInbox
1677-
availableProjects={projects}
1677+
availableProjects={allProjects}
16781678
defaultProject={selectedProject || ''}
16791679
/>
16801680

shared/a11y-patterns-detailed.md

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
# Accessibility (a11y) Patterns — Detailed Reference
2+
3+
Full reference for fixing Svelte a11y build warnings. For the quick cheat sheet loaded in project context, see `a11y-patterns.md`.
4+
5+
---
6+
7+
## Rule 1: Always Associate Labels with Inputs
8+
9+
Every `<label>` must be linked to its control. There are two valid patterns:
10+
11+
```svelte
12+
<!-- ✅ CORRECT: for= points to input's id -->
13+
<label for="email">Email</label>
14+
<input id="email" type="email" bind:value={email} />
15+
16+
<!-- ✅ CORRECT: label wraps the input -->
17+
<label>
18+
Email
19+
<input type="email" bind:value={email} />
20+
</label>
21+
22+
<!-- ❌ WRONG: label has no association -->
23+
<label>Email</label>
24+
<input type="email" bind:value={email} />
25+
26+
<!-- ❌ WRONG: label has for= but input has no matching id -->
27+
<label for="email">Email</label>
28+
<input type="email" bind:value={email} />
29+
```
30+
31+
**DaisyUI form fields**`<label class="label">` is a layout wrapper, not a semantic label. It still needs `for=`:
32+
```svelte
33+
<!-- ✅ DaisyUI pattern -->
34+
<label class="label" for="phone">
35+
<span class="label-text">Phone</span>
36+
</label>
37+
<input id="phone" class="input" type="tel" bind:value={phone} />
38+
39+
<!-- ✅ DaisyUI checkbox/toggle — label wraps the input -->
40+
<label class="flex items-center gap-2" for="active">
41+
<input id="active" type="checkbox" class="checkbox" bind:checked={active} />
42+
<span>Active</span>
43+
</label>
44+
```
45+
46+
---
47+
48+
## Rule 2: Never Use `<div>` for Click Interactions
49+
50+
Clickable `<div>` elements are invisible to keyboard users and screen readers. Always use semantic elements.
51+
52+
```svelte
53+
<!-- ✅ CORRECT: use <button> for actions -->
54+
<button type="button" onclick={() => select(item)}>
55+
{item.name}
56+
</button>
57+
58+
<!-- ✅ CORRECT: use <a> for navigation -->
59+
<a href="/jobs/{id}">{title}</a>
60+
61+
<!-- ❌ WRONG: div with onclick -->
62+
<div onclick={() => select(item)}>{item.name}</div>
63+
64+
<!-- ❌ WRONG: span with onclick -->
65+
<span onclick={handleClick}>Click me</span>
66+
```
67+
68+
**If you must use a non-button element** (e.g., a card that's both a link and has internal actions), add role + tabindex + keyboard handler:
69+
```svelte
70+
<!-- ✅ Only when <button> truly won't work -->
71+
<div
72+
role="button"
73+
tabindex="0"
74+
onclick={handleClick}
75+
onkeydown={(e) => e.key === 'Enter' && handleClick()}
76+
>
77+
...
78+
</div>
79+
```
80+
81+
---
82+
83+
## Rule 3: Give Every Input an Accessible Name
84+
85+
Inputs without visible labels need `aria-label` or `aria-labelledby`.
86+
87+
```svelte
88+
<!-- ✅ Icon-only search input -->
89+
<input type="search" aria-label="Search jobs" bind:value={query} />
90+
91+
<!-- ✅ Label provided by nearby heading -->
92+
<h2 id="filters-heading">Filters</h2>
93+
<input aria-labelledby="filters-heading" type="text" />
94+
95+
<!-- ✅ Placeholder is NOT a label — still need aria-label -->
96+
<input
97+
type="text"
98+
placeholder="Search..."
99+
aria-label="Search"
100+
bind:value={query}
101+
/>
102+
103+
<!-- ❌ WRONG: no label, no aria-label -->
104+
<input type="text" placeholder="Search..." bind:value={query} />
105+
```
106+
107+
---
108+
109+
## Rule 4: Interactive ARIA Roles Need tabindex
110+
111+
When you assign an interactive ARIA role, the element must be focusable.
112+
113+
```svelte
114+
<!-- ✅ CORRECT -->
115+
<div role="menu" tabindex="0">...</div>
116+
<div role="dialog" tabindex="-1">...</div>
117+
<div role="button" tabindex="0" onclick={...} onkeydown={...}>...</div>
118+
119+
<!-- ❌ WRONG: role without tabindex -->
120+
<div role="menu">...</div>
121+
```
122+
123+
**tabindex values:**
124+
- `tabindex="0"` — in tab order (user can tab to it)
125+
- `tabindex="-1"` — focusable by script, not in tab order (good for modals)
126+
127+
---
128+
129+
## Rule 5: Image Alt Text Rules
130+
131+
```svelte
132+
<!-- ✅ Descriptive alt for informational images -->
133+
<img src={avatar} alt="Profile photo of {name}" />
134+
135+
<!-- ✅ Empty alt for decorative images (screen reader skips it) -->
136+
<img src={decorative} alt="" />
137+
138+
<!-- ❌ WRONG: redundant words — screen readers say "image" automatically -->
139+
<img src={photo} alt="image of the job site" />
140+
<img src={photo} alt="photo of technician" />
141+
<img src={photo} alt="picture showing invoice" />
142+
```
143+
144+
---
145+
146+
## Rule 6: Video Elements Need Caption Tracks
147+
148+
```svelte
149+
<!-- ✅ With real captions -->
150+
<video src={url} controls>
151+
<track kind="captions" src={captionsUrl} srclang="en" label="English" />
152+
</video>
153+
154+
<!-- ✅ Placeholder when captions unavailable (suppresses warning) -->
155+
<video src={url} controls>
156+
<track kind="captions" src="" srclang="en" label="English" default />
157+
</video>
158+
```
159+
160+
---
161+
162+
## Quick Reference: Warning → Fix
163+
164+
| Svelte Warning | Fix |
165+
|----------------|-----|
166+
| `a11y_label_has_associated_control` | Add `for=` to `<label>` matching `id=` on input, or wrap input inside label |
167+
| `a11y_click_events_have_key_events` | Replace `<div onclick>` with `<button type="button">` |
168+
| `a11y_no_static_element_interactions` | Replace `<div onclick>` with `<button type="button">` |
169+
| `a11y_consider_explicit_label` | Add `aria-label` or `<label>` to input |
170+
| `a11y_no_noninteractive_element_interactions` | Use semantic element (`<button>`, `<a>`) or add proper role |
171+
| `a11y_no_noninteractive_tabindex` | Remove `tabindex` from non-interactive elements, or give them an interactive role |
172+
| `a11y_interactive_supports_focus` | Add `tabindex="0"` to element with interactive ARIA role |
173+
| `a11y_media_has_caption` | Add `<track kind="captions">` inside `<video>` |
174+
| `a11y_img_redundant_alt` | Remove "image", "photo", "picture" from alt text |
175+
| `a11y_role_supports_aria_props` | Remove ARIA attributes invalid for the element's role |

shared/a11y-patterns.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Accessibility (a11y) Patterns
2+
3+
Prevents common Svelte a11y build warnings. Follow these when writing any UI code.
4+
5+
**Labels must be associated with inputs** — use `for=`/`id` or wrap:
6+
```svelte
7+
<label for="email">Email</label><input id="email" ... /> <!-- ✅ -->
8+
<label>Email <input .../></label> <!-- ✅ -->
9+
<label>Email</label><input ... /> <!-- ❌ -->
10+
```
11+
12+
**Never use `<div onclick>` — use `<button type="button">`:**
13+
```svelte
14+
<button type="button" onclick={fn}>...</button> <!-- ✅ -->
15+
<div onclick={fn}>...</div> <!-- ❌ -->
16+
```
17+
18+
**Inputs without visible labels need `aria-label`:**
19+
```svelte
20+
<input type="search" aria-label="Search jobs" /> <!-- ✅ -->
21+
<input type="search" placeholder="Search..." /> <!-- ❌ -->
22+
```
23+
24+
**Interactive ARIA roles need `tabindex`:**
25+
```svelte
26+
<div role="menu" tabindex="0">...</div> <!-- ✅ -->
27+
<div role="dialog" tabindex="-1">...</div> <!-- ✅ -->
28+
<div role="menu">...</div> <!-- ❌ -->
29+
```
30+
31+
**Video needs a caption track. Image alt must not contain "image/photo/picture".**
32+
33+
| Warning | Fix |
34+
|---------|-----|
35+
| `a11y_label_has_associated_control` | Add `for=` on label + `id=` on input |
36+
| `a11y_click_events_have_key_events` | Replace `<div onclick>` with `<button type="button">` |
37+
| `a11y_no_static_element_interactions` | Replace `<div onclick>` with `<button type="button">` |
38+
| `a11y_consider_explicit_label` | Add `aria-label` to input |
39+
| `a11y_interactive_supports_focus` | Add `tabindex="0"` to element with interactive role |
40+
| `a11y_media_has_caption` | Add `<track kind="captions">` inside `<video>` |
41+
| `a11y_img_redundant_alt` | Remove "image/photo/picture" from alt text |

0 commit comments

Comments
 (0)