Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .Jules/palette.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 2024-08-16 - Dynamic ARIA labels for Toggle Buttons
**Learning:** When a toggle button switches its icon based on state (e.g., Eye/EyeOff for passwords, or a generic toggle without text), a static `aria-label` is insufficient because the action the button performs changes depending on the current state.
**Action:** Use a dynamic Vue binding (`:aria-label="condition ? 'Action A' : 'Action B'"`) corresponding to the boolean state of the toggle, ensuring screen readers announce the exact action the user will perform by clicking the button.
17 changes: 12 additions & 5 deletions src/components/settings/tabs/AISettingsTab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -587,14 +587,19 @@ async function onClearMemories() {
<div class="groq-key-row">
<div class="groq-key-input-wrapper">
<input
:type="showGroqKey ? 'text' : 'password'"
v-model="groqKeyInput"
:type="showGroqKey ? 'text' : 'password'"
placeholder="gsk_..."
class="groq-key-input"
spellcheck="false"
autocomplete="off"
/>
<button class="key-toggle-btn" @click="showGroqKey = !showGroqKey" :title="showGroqKey ? 'Hide' : 'Show'">
>
<button
class="key-toggle-btn"
:aria-label="showGroqKey ? 'Hide API key' : 'Show API key'"
:title="showGroqKey ? 'Hide' : 'Show'"
@click="showGroqKey = !showGroqKey"
>
<EyeOff v-if="showGroqKey" :size="14" />
<Eye v-else :size="14" />
</button>
Expand Down Expand Up @@ -745,7 +750,7 @@ async function onClearMemories() {
step="1"
:value="(settingsStore.aiMonthlyBudgetCents / 100).toFixed(0)"
@change="(e: Event) => settingsStore.updateSetting('aiMonthlyBudgetCents', Math.round(parseFloat((e.target as HTMLInputElement).value || '0') * 100))"
/>
>
<span class="budget-hint">/ month (informational)</span>
</div>
</div>
Expand Down Expand Up @@ -1021,7 +1026,9 @@ async function onClearMemories() {
<HeartPulse :size="14" :class="{ spinning: memoryHealthRunning }" />
{{ memoryHealthRunning ? 'Checking...' : 'Run Quick Check' }}
</button>
<p class="mh-hint">Full assessment available in AI Hub &gt; Memory tab.</p>
<p class="mh-hint">
Full assessment available in AI Hub &gt; Memory tab.
</p>

<div class="ai-memory-debug" data-testid="ai-memory-debug">
<div class="ai-memory-debug-header">
Expand Down
9 changes: 7 additions & 2 deletions src/components/settings/tabs/AccountSettingsTab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ const handleChangePassword = async () => {
class="form-input"
placeholder="Enter new password"
>
<button class="toggle-visibility" @click="showPassword = !showPassword">
<button class="toggle-visibility" :aria-label="showPassword ? 'Hide password' : 'Show password'" @click="showPassword = !showPassword">
<Eye v-if="!showPassword" :size="16" />
<EyeOff v-else :size="16" />
</button>
Expand Down Expand Up @@ -504,7 +504,12 @@ const handleChangePassword = async () => {
class="form-input"
@focus="(e) => (e.target as HTMLInputElement).select()"
>
<button class="toggle-visibility" :title="localApiCopied ? 'Copied!' : 'Copy'" @click="copyLocalApiToken">
<button
class="toggle-visibility"
:aria-label="localApiCopied ? 'Copied' : 'Copy API token'"
:title="localApiCopied ? 'Copied!' : 'Copy'"
@click="copyLocalApiToken"
>
<Check v-if="localApiCopied" :size="16" />
<Copy v-else :size="16" />
</button>
Expand Down
Loading