Skip to content
Merged
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
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ A developer-facing visual theming system for Saucebase. Lets the SaaS owner desi
- Google Fonts — loaded on-demand via `<link>` injection; font classes injected per-var
- Theme picker with animated ripple transition between themes
- Theme persistence via `localStorage` key `sb-theme-theme`
- Save button is a **dropdown**: "Save" (update existing custom theme) / "Save as new theme" (create new)
- Save button is a **dropdown**: "Save" (update existing custom theme) / "Save as" (create new)

---

Expand Down
16 changes: 8 additions & 8 deletions resources/js/components/ThemePanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -822,12 +822,12 @@ const dialogCommandOpen = ref(false);
{{ $t('Theme customizer') }}
</SheetTitle>
<SheetDescription class="sr-only">
{{ $t('Adjust colors, font, and radius.') }}
{{ $t('Add your own flavor') }}

Copilot AI Apr 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SheetDescription is visually hidden (sr-only) and typically provides the dialog’s accessible description. Replacing the functional description with a slogan (“Add your own flavor”) makes the accessible description less informative for screen-reader users; consider keeping a descriptive sentence about what can be customized (and optionally use the slogan in visible text instead).

Suggested change
{{ $t('Add your own flavor') }}
{{
$t(
'Customize your theme colors, fonts, spacing, and other appearance settings.',
)
}}

Copilot uses AI. Check for mistakes.
</SheetDescription>

<!-- Header -->
<div
class="border-border flex items-center gap-3 border-b px-4 py-3"
class="border-border flex flex-wrap items-center gap-x-3 gap-y-2 border-b px-4 py-3"
>
<IconPalette
class="text-muted-foreground size-5 shrink-0"
Expand All @@ -839,13 +839,13 @@ const dialogCommandOpen = ref(false);
{{ $t('Theme customizer') }}
</p>
<p class="text-muted-foreground mt-0.5 text-xs">
{{ $t('Adjust colors, font, and radius.') }}
{{ $t('Add your own flavor') }}
</p>
</div>
<ThemeSelector inline hide-device />
<ThemeSelector class="order-4 sm:order-3 w-full sm:w-auto" inline hide-device />
<button
data-testid="theme-panel-close"
class="text-muted-foreground hover:bg-accent hover:text-accent-foreground focus-visible:ring-ring rounded-md p-1 transition-colors focus-visible:ring-2 focus-visible:outline-none"
class="order-3 sm:order-4 text-muted-foreground hover:bg-accent hover:text-accent-foreground focus-visible:ring-ring rounded-md p-1 transition-colors focus-visible:ring-2 focus-visible:outline-none"
:aria-label="$t('Close theme panel')"
@click="sheetOpen = false"
>
Expand Down Expand Up @@ -1077,7 +1077,7 @@ const dialogCommandOpen = ref(false);
<IconRotateCcw class="size-4" />
{{ $t('Reset') }}
</button>
<!-- Split button: custom theme → Save + dropdown; built-in → Save as new theme only -->
<!-- Split button: custom theme → Save + dropdown; built-in → Save as only -->
<div
v-if="currentTheme?.editable"
class="border-border flex flex-1 overflow-hidden rounded-lg border"
Expand All @@ -1104,7 +1104,7 @@ const dialogCommandOpen = ref(false);
data-testid="theme-panel-save-as"
@click="dialogSaveOpen = true"
>
{{ $t('Save as new theme') }}
{{ $t('Save as') }}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
Expand All @@ -1116,7 +1116,7 @@ const dialogCommandOpen = ref(false);
@click="dialogSaveOpen = true"
>
<IconSave class="size-4" />
{{ $t('Save as new theme') }}
{{ $t('Save as') }}
</button>
<Tooltip>
<TooltipTrigger as-child>
Expand Down
9 changes: 4 additions & 5 deletions tests/e2e/themes-config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,25 @@ import { expect, test } from '@e2e/fixtures';
test.describe('Themes config flags', () => {
test.beforeEach(async ({ laravel }) => {
await laravel.callFunction('Modules\\Themes\\Tests\\Support\\ThemesTestHelper::cleanUserThemes');
// Restore defaults after any config overrides
await laravel.callFunction('Modules\\Themes\\Tests\\Support\\ThemesTestHelper::resetConfig');
await laravel.config('themes.enabled', true);
});

test.afterEach(async ({ laravel }) => {
await laravel.callFunction('Modules\\Themes\\Tests\\Support\\ThemesTestHelper::cleanUserThemes');
await laravel.callFunction('Modules\\Themes\\Tests\\Support\\ThemesTestHelper::resetConfig');
await laravel.config('themes.enabled', true);
});

// ── themes.enabled ────────────────────────────────────────────────────────

test('theme panel trigger is hidden when themes.enabled is false', async ({ page, laravel }) => {
await laravel.callFunction('Modules\\Themes\\Tests\\Support\\ThemesTestHelper::setEnabled', [false]);
await laravel.config('themes.enabled', false);
await page.goto('/');

await expect(page.getByTestId('theme-panel-trigger')).not.toBeVisible();
});

test('theme panel trigger is visible when themes.enabled is true', async ({ page, laravel }) => {
await laravel.callFunction('Modules\\Themes\\Tests\\Support\\ThemesTestHelper::setEnabled', [true]);
await laravel.config('themes.enabled', true);
await page.goto('/');

await expect(page.getByTestId('theme-panel-trigger')).toBeVisible();
Expand Down
Loading