From 02c82c75475cdd3c251ca4c0e3d4b9e4696dda9a Mon Sep 17 00:00:00 2001 From: Till-JS <101404291+Till-JS@users.noreply.github.com> Date: Tue, 2 Dec 2025 22:56:09 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(a11y):=20add=20accessibility?= =?UTF-8?q?=20settings=20and=20theme=20improvements?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add comprehensive accessibility support across shared packages: - A11y store with contrast, colorblind mode, and reduce motion settings - A11yQuickToggles and A11ySettings UI components - PillNavigation and PillDropdown components in shared-ui - Calendar app updates to integrate new theme/a11y features 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- COMMANDS.md | 1 + .../src/calendar/calendar.controller.ts | 9 +- .../backend/src/calendar/calendar.service.ts | 2 +- apps/calendar/apps/web/src/app.css | 58 ++-- .../components/calendar/CalendarHeader.svelte | 16 +- .../calendar/CalendarSidebar.svelte | 18 +- .../lib/components/calendar/DayView.svelte | 10 +- .../components/calendar/MiniCalendar.svelte | 20 +- .../lib/components/calendar/MonthView.svelte | 20 +- .../lib/components/calendar/WeekView.svelte | 20 +- .../src/lib/components/event/EventForm.svelte | 12 +- .../web/src/lib/stores/calendars.svelte.ts | 12 +- .../apps/web/src/lib/stores/events.svelte.ts | 16 +- .../apps/web/src/routes/+layout.svelte | 2 +- .../calendar/apps/web/src/routes/+page.svelte | 4 +- .../apps/web/src/routes/agenda/+page.svelte | 26 +- .../web/src/routes/calendars/+page.svelte | 10 +- .../web/src/routes/event/[id]/+page.svelte | 10 +- .../web/src/routes/event/new/+page.svelte | 2 +- .../apps/web/src/routes/settings/+page.svelte | 32 +- .../src/components/A11yQuickToggles.svelte | 114 +++++++ .../src/components/A11ySettings.svelte | 188 +++++++++++ packages/shared-theme-ui/src/index.ts | 16 +- .../src/pages/ThemePage.svelte | 26 +- packages/shared-theme-ui/src/types.ts | 55 +++ packages/shared-theme/src/a11y-constants.ts | 132 ++++++++ .../shared-theme/src/a11y-store.svelte.ts | 192 +++++++++++ packages/shared-theme/src/a11y-utils.ts | 312 ++++++++++++++++++ packages/shared-theme/src/index.ts | 33 ++ packages/shared-theme/src/types.ts | 69 ++++ packages/shared-theme/src/utils.ts | 15 +- .../src/navigation/PillDropdown.svelte | 40 +++ .../src/navigation/PillNavigation.svelte | 125 +++++++ 33 files changed, 1474 insertions(+), 143 deletions(-) create mode 100644 packages/shared-theme-ui/src/components/A11yQuickToggles.svelte create mode 100644 packages/shared-theme-ui/src/components/A11ySettings.svelte create mode 100644 packages/shared-theme/src/a11y-constants.ts create mode 100644 packages/shared-theme/src/a11y-store.svelte.ts create mode 100644 packages/shared-theme/src/a11y-utils.ts diff --git a/COMMANDS.md b/COMMANDS.md index 349a90584..79edfa683 100644 --- a/COMMANDS.md +++ b/COMMANDS.md @@ -9,6 +9,7 @@ pnpm docker:down pnpm dev:chat:app pnpm dev:contacts:app +pnpm dev:storage:app pnpm dev:calendar:app pnpm dev:picture:app pnpm dev:manacore:app diff --git a/apps/calendar/apps/backend/src/calendar/calendar.controller.ts b/apps/calendar/apps/backend/src/calendar/calendar.controller.ts index 54f19297d..18d3489ec 100644 --- a/apps/calendar/apps/backend/src/calendar/calendar.controller.ts +++ b/apps/calendar/apps/backend/src/calendar/calendar.controller.ts @@ -19,7 +19,14 @@ export class CalendarController { @Get() async findAll(@CurrentUser() user: CurrentUserData) { - const calendars = await this.calendarService.findAll(user.userId); + let calendars = await this.calendarService.findAll(user.userId); + + // Lazy creation: if no calendars exist, create a default one + if (calendars.length === 0) { + const defaultCalendar = await this.calendarService.getOrCreateDefaultCalendar(user.userId); + calendars = [defaultCalendar]; + } + return { calendars }; } diff --git a/apps/calendar/apps/backend/src/calendar/calendar.service.ts b/apps/calendar/apps/backend/src/calendar/calendar.service.ts index a7ae8cbd2..44ba6de0e 100644 --- a/apps/calendar/apps/backend/src/calendar/calendar.service.ts +++ b/apps/calendar/apps/backend/src/calendar/calendar.service.ts @@ -116,7 +116,7 @@ export class CalendarService { // Create a new default calendar return this.create(userId, { - name: 'My Calendar', + name: 'Mein Kalender', isDefault: true, color: '#3B82F6', }); diff --git a/apps/calendar/apps/web/src/app.css b/apps/calendar/apps/web/src/app.css index f654d8e99..31d956b21 100644 --- a/apps/calendar/apps/web/src/app.css +++ b/apps/calendar/apps/web/src/app.css @@ -40,18 +40,18 @@ /* Hour slot in day/week view */ .hour-slot { height: var(--hour-height); - border-bottom: 1px solid hsl(var(--border) / 0.5); + border-bottom: 1px solid hsl(var(--color-border) / 0.5); position: relative; } .hour-slot:hover { - background-color: hsl(var(--muted) / 0.3); + background-color: hsl(var(--color-muted) / 0.3); } /* Event card in calendar */ .event-card { - background-color: hsl(var(--primary)); - color: hsl(var(--primary-foreground)); + background-color: hsl(var(--color-primary)); + color: hsl(var(--color-primary-foreground)); border-radius: var(--radius-sm); padding: 2px 6px; font-size: 0.75rem; @@ -68,17 +68,17 @@ /* Day cell in month view */ .day-cell { min-height: 100px; - border: 1px solid hsl(var(--border)); + border: 1px solid hsl(var(--color-border)); padding: var(--spacing-xs); transition: background-color var(--transition-fast); } .day-cell:hover { - background-color: hsl(var(--muted) / 0.3); + background-color: hsl(var(--color-muted) / 0.3); } .day-cell.today { - background-color: hsl(var(--primary) / 0.1); + background-color: hsl(var(--color-primary) / 0.1); } .day-cell.other-month { @@ -91,7 +91,7 @@ left: 0; right: 0; height: 2px; - background-color: hsl(var(--destructive)); + background-color: hsl(var(--color-error)); z-index: 10; } @@ -103,7 +103,7 @@ width: 10px; height: 10px; border-radius: 50%; - background-color: hsl(var(--destructive)); + background-color: hsl(var(--color-error)); } /* Mini calendar */ @@ -123,24 +123,24 @@ } .mini-calendar .day:hover { - background-color: hsl(var(--muted)); + background-color: hsl(var(--color-muted)); } .mini-calendar .day.today { - background-color: hsl(var(--primary)); - color: hsl(var(--primary-foreground)); + background-color: hsl(var(--color-primary)); + color: hsl(var(--color-primary-foreground)); } .mini-calendar .day.selected { - border: 2px solid hsl(var(--primary)); + border: 2px solid hsl(var(--color-primary)); } /* Card styles */ .card { - background-color: hsl(var(--card)); + background-color: hsl(var(--color-surface)); border-radius: var(--radius-lg); padding: var(--spacing-lg); - border: 1px solid hsl(var(--border)); + border: 1px solid hsl(var(--color-border)); } /* Button styles */ @@ -159,12 +159,12 @@ } .btn-primary { - background: hsl(var(--primary)); - color: hsl(var(--primary-foreground)); + background: hsl(var(--color-primary)); + color: hsl(var(--color-primary-foreground)); } .btn-primary:hover { - background: hsl(var(--primary) / 0.9); + background: hsl(var(--color-primary) / 0.9); } .btn-primary:disabled { @@ -173,21 +173,21 @@ } .btn-secondary { - background: hsl(var(--secondary)); - color: hsl(var(--secondary-foreground)); + background: hsl(var(--color-secondary)); + color: hsl(var(--color-secondary-foreground)); } .btn-secondary:hover { - background: hsl(var(--secondary) / 0.8); + background: hsl(var(--color-secondary) / 0.8); } .btn-ghost { background: transparent; - color: hsl(var(--foreground)); + color: hsl(var(--color-foreground)); } .btn-ghost:hover { - background: hsl(var(--muted)); + background: hsl(var(--color-muted)); } .btn-icon { @@ -204,21 +204,21 @@ display: block; width: 100%; padding: 0.5rem 0.75rem; - border: 2px solid hsl(var(--border)); + border: 2px solid hsl(var(--color-border)); border-radius: var(--radius-md); - background-color: hsl(var(--background)); - color: hsl(var(--foreground)); + background-color: hsl(var(--color-background)); + color: hsl(var(--color-foreground)); font-size: 0.875rem; transition: border-color var(--transition-fast); } .input:focus { outline: none; - border-color: hsl(var(--primary)); + border-color: hsl(var(--color-primary)); } .input::placeholder { - color: hsl(var(--muted-foreground)); + color: hsl(var(--color-muted-foreground)); } /* Select styling */ @@ -233,7 +233,7 @@ select.input { /* Text colors */ .text-destructive { - color: hsl(var(--destructive)); + color: hsl(var(--color-error)); } /* Scrollbar styling */ diff --git a/apps/calendar/apps/web/src/lib/components/calendar/CalendarHeader.svelte b/apps/calendar/apps/web/src/lib/components/calendar/CalendarHeader.svelte index 92b511c70..a3c930b3f 100644 --- a/apps/calendar/apps/web/src/lib/components/calendar/CalendarHeader.svelte +++ b/apps/calendar/apps/web/src/lib/components/calendar/CalendarHeader.svelte @@ -85,8 +85,8 @@ align-items: center; justify-content: space-between; padding: 1rem 1.5rem; - border-bottom: 1px solid hsl(var(--border)); - background: hsl(var(--card)); + border-bottom: 1px solid hsl(var(--color-border)); + background: hsl(var(--color-surface)); } .header-left { @@ -103,7 +103,7 @@ .header-title { font-size: 1.25rem; font-weight: 600; - color: hsl(var(--foreground)); + color: hsl(var(--color-foreground)); margin: 0; } @@ -115,7 +115,7 @@ .view-selector { display: flex; - background: hsl(var(--muted)); + background: hsl(var(--color-muted)); border-radius: var(--radius-md); padding: 0.25rem; } @@ -127,18 +127,18 @@ border-radius: var(--radius-sm); font-size: 0.875rem; font-weight: 500; - color: hsl(var(--muted-foreground)); + color: hsl(var(--color-muted-foreground)); cursor: pointer; transition: all 150ms ease; } .view-btn:hover { - color: hsl(var(--foreground)); + color: hsl(var(--color-foreground)); } .view-btn.active { - background: hsl(var(--background)); - color: hsl(var(--foreground)); + background: hsl(var(--color-background)); + color: hsl(var(--color-foreground)); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); } diff --git a/apps/calendar/apps/web/src/lib/components/calendar/CalendarSidebar.svelte b/apps/calendar/apps/web/src/lib/components/calendar/CalendarSidebar.svelte index 0cef56601..bffdc81ff 100644 --- a/apps/calendar/apps/web/src/lib/components/calendar/CalendarSidebar.svelte +++ b/apps/calendar/apps/web/src/lib/components/calendar/CalendarSidebar.svelte @@ -43,9 +43,9 @@ diff --git a/apps/calendar/apps/web/src/lib/components/calendar/MonthView.svelte b/apps/calendar/apps/web/src/lib/components/calendar/MonthView.svelte index d71928f6e..9eb145b29 100644 --- a/apps/calendar/apps/web/src/lib/components/calendar/MonthView.svelte +++ b/apps/calendar/apps/web/src/lib/components/calendar/MonthView.svelte @@ -126,7 +126,7 @@ .weekday-headers { display: grid; grid-template-columns: repeat(7, 1fr); - border-bottom: 1px solid hsl(var(--border)); + border-bottom: 1px solid hsl(var(--color-border)); } .weekday-header { @@ -134,7 +134,7 @@ text-align: center; font-size: 0.75rem; font-weight: 600; - color: hsl(var(--muted-foreground)); + color: hsl(var(--color-muted-foreground)); text-transform: uppercase; } @@ -152,7 +152,7 @@ } .day-cell { - border: 1px solid hsl(var(--border)); + border: 1px solid hsl(var(--color-border)); border-top: none; border-left: none; padding: var(--spacing-xs); @@ -165,15 +165,15 @@ } .day-cell:first-child { - border-left: 1px solid hsl(var(--border)); + border-left: 1px solid hsl(var(--color-border)); } .day-cell:hover { - background-color: hsl(var(--muted) / 0.3); + background-color: hsl(var(--color-muted) / 0.3); } .day-cell.today { - background-color: hsl(var(--primary) / 0.1); + background-color: hsl(var(--color-primary) / 0.1); } .day-cell.other-month { @@ -193,8 +193,8 @@ } .day-number.today { - background: hsl(var(--primary)); - color: hsl(var(--primary-foreground)); + background: hsl(var(--color-primary)); + color: hsl(var(--color-primary-foreground)); } .day-events { @@ -232,7 +232,7 @@ .more-events { font-size: 0.75rem; - color: hsl(var(--muted-foreground)); + color: hsl(var(--color-muted-foreground)); background: transparent; border: none; cursor: pointer; @@ -241,6 +241,6 @@ } .more-events:hover { - color: hsl(var(--primary)); + color: hsl(var(--color-primary)); } diff --git a/apps/calendar/apps/web/src/lib/components/calendar/WeekView.svelte b/apps/calendar/apps/web/src/lib/components/calendar/WeekView.svelte index 7f9aedb14..55153fcbe 100644 --- a/apps/calendar/apps/web/src/lib/components/calendar/WeekView.svelte +++ b/apps/calendar/apps/web/src/lib/components/calendar/WeekView.svelte @@ -162,7 +162,7 @@ .all-day-row { display: flex; - border-bottom: 1px solid hsl(var(--border)); + border-bottom: 1px solid hsl(var(--color-border)); min-height: 32px; } @@ -172,7 +172,7 @@ flex-wrap: wrap; gap: 2px; padding: 4px; - border-left: 1px solid hsl(var(--border)); + border-left: 1px solid hsl(var(--color-border)); } .all-day-event { @@ -189,7 +189,7 @@ .day-headers { display: flex; - border-bottom: 1px solid hsl(var(--border)); + border-bottom: 1px solid hsl(var(--color-border)); } .time-gutter { @@ -203,12 +203,12 @@ flex-direction: column; align-items: center; padding: 0.5rem; - border-left: 1px solid hsl(var(--border)); + border-left: 1px solid hsl(var(--color-border)); } .day-name { font-size: 0.75rem; - color: hsl(var(--muted-foreground)); + color: hsl(var(--color-muted-foreground)); text-transform: uppercase; } @@ -224,8 +224,8 @@ } .day-number.today { - background: hsl(var(--primary)); - color: hsl(var(--primary-foreground)); + background: hsl(var(--color-primary)); + color: hsl(var(--color-primary-foreground)); } .time-grid { @@ -244,7 +244,7 @@ padding-right: 0.5rem; text-align: right; font-size: 0.75rem; - color: hsl(var(--muted-foreground)); + color: hsl(var(--color-muted-foreground)); position: relative; top: -0.5em; } @@ -257,11 +257,11 @@ .day-column { flex: 1; position: relative; - border-left: 1px solid hsl(var(--border)); + border-left: 1px solid hsl(var(--color-border)); } .day-column.today { - background: hsl(var(--primary) / 0.05); + background: hsl(var(--color-primary) / 0.05); } .event-card { diff --git a/apps/calendar/apps/web/src/lib/components/event/EventForm.svelte b/apps/calendar/apps/web/src/lib/components/event/EventForm.svelte index 6a2d07e10..59c3d2711 100644 --- a/apps/calendar/apps/web/src/lib/components/event/EventForm.svelte +++ b/apps/calendar/apps/web/src/lib/components/event/EventForm.svelte @@ -18,7 +18,14 @@ let description = $state(event?.description || ''); let location = $state(event?.location || ''); let isAllDay = $state(event?.isAllDay || false); - let calendarId = $state(event?.calendarId || calendarsStore.defaultCalendar?.id || ''); + let calendarId = $state(event?.calendarId || ''); + + // Set default calendar when calendars are loaded + $effect(() => { + if (!calendarId && calendarsStore.defaultCalendar?.id) { + calendarId = calendarsStore.defaultCalendar.id; + } + }); // Date/time handling let startDate = $state(''); @@ -58,6 +65,7 @@ e.preventDefault(); if (!title.trim()) return; + if (!calendarId) return; const startDateTime = new Date(`${startDate}T${isAllDay ? '00:00' : startTime}`); const endDateTime = new Date(`${endDate}T${isAllDay ? '23:59' : endTime}`); @@ -158,7 +166,7 @@ - diff --git a/apps/calendar/apps/web/src/lib/stores/calendars.svelte.ts b/apps/calendar/apps/web/src/lib/stores/calendars.svelte.ts index 665f3910d..c2e366be1 100644 --- a/apps/calendar/apps/web/src/lib/stores/calendars.svelte.ts +++ b/apps/calendar/apps/web/src/lib/stores/calendars.svelte.ts @@ -56,7 +56,9 @@ export const calendarsStore = { error = result.error.message; calendars = []; } else { - calendars = result.data || []; + // API returns { calendars: [...] } + const data = result.data as { calendars: Calendar[] } | null; + calendars = data?.calendars || []; } loading = false; @@ -70,7 +72,9 @@ export const calendarsStore = { const result = await api.createCalendar(data); if (result.data) { - calendars = [...calendars, result.data]; + // API returns { calendar: {...} } + const responseData = result.data as { calendar: Calendar }; + calendars = [...calendars, responseData.calendar]; } return result; @@ -83,7 +87,9 @@ export const calendarsStore = { const result = await api.updateCalendar(id, data); if (result.data) { - calendars = getCalendarsArray().map((c) => (c.id === id ? result.data! : c)); + // API returns { calendar: {...} } + const responseData = result.data as { calendar: Calendar }; + calendars = getCalendarsArray().map((c) => (c.id === id ? responseData.calendar : c)); } return result; diff --git a/apps/calendar/apps/web/src/lib/stores/events.svelte.ts b/apps/calendar/apps/web/src/lib/stores/events.svelte.ts index 69776c8c9..1bb8544d8 100644 --- a/apps/calendar/apps/web/src/lib/stores/events.svelte.ts +++ b/apps/calendar/apps/web/src/lib/stores/events.svelte.ts @@ -13,9 +13,9 @@ let error = $state(null); let loadedRange = $state<{ start: Date; end: Date } | null>(null); export const eventsStore = { - // Getters + // Getters - always return safe values get events() { - return events; + return events ?? []; }, get loading() { return loading; @@ -40,7 +40,9 @@ export const eventsStore = { if (result.error) { error = result.error.message; } else { - events = result.data || []; + // API returns { events: [...] } + const data = result.data as { events: CalendarEvent[] } | null; + events = data?.events || []; loadedRange = { start: startDate, end: endDate }; } @@ -94,7 +96,9 @@ export const eventsStore = { const result = await api.createEvent(data); if (result.data) { - events = [...events, result.data]; + // API returns { event: {...} } + const responseData = result.data as { event: CalendarEvent }; + events = [...events, responseData.event]; } return result; @@ -107,7 +111,9 @@ export const eventsStore = { const result = await api.updateEvent(id, data); if (result.data) { - events = events.map((e) => (e.id === id ? result.data! : e)); + // API returns { event: {...} } + const responseData = result.data as { event: CalendarEvent }; + events = events.map((e) => (e.id === id ? responseData.event : e)); } return result; diff --git a/apps/calendar/apps/web/src/routes/+layout.svelte b/apps/calendar/apps/web/src/routes/+layout.svelte index 13293128b..c9535d247 100644 --- a/apps/calendar/apps/web/src/routes/+layout.svelte +++ b/apps/calendar/apps/web/src/routes/+layout.svelte @@ -77,7 +77,7 @@ const navItems: PillNavItem[] = [ { href: '/', label: 'Kalender', icon: 'calendar' }, { href: '/agenda', label: 'Agenda', icon: 'list' }, - { href: '/calendars', label: 'Meine Kalender', icon: 'document' }, + { href: '/calendars', label: 'Meine Kalender', icon: 'folder' }, { href: '/settings', label: 'Einstellungen', icon: 'settings' }, { href: '/feedback', label: 'Feedback', icon: 'chat' }, ]; diff --git a/apps/calendar/apps/web/src/routes/+page.svelte b/apps/calendar/apps/web/src/routes/+page.svelte index aac980ca4..218311c9e 100644 --- a/apps/calendar/apps/web/src/routes/+page.svelte +++ b/apps/calendar/apps/web/src/routes/+page.svelte @@ -107,9 +107,9 @@ display: flex; flex-direction: column; min-width: 0; - background: hsl(var(--card)); + background: hsl(var(--color-surface)); border-radius: var(--radius-lg); - border: 1px solid hsl(var(--border)); + border: 1px solid hsl(var(--color-border)); overflow: hidden; } diff --git a/apps/calendar/apps/web/src/routes/agenda/+page.svelte b/apps/calendar/apps/web/src/routes/agenda/+page.svelte index 29f2acf9c..ebcb1d3e5 100644 --- a/apps/calendar/apps/web/src/routes/agenda/+page.svelte +++ b/apps/calendar/apps/web/src/routes/agenda/+page.svelte @@ -11,9 +11,13 @@ // Group events by date let groupedEvents = $derived.by(() => { - const groups: Map = new Map(); + // Safety check: ensure events is an array + const currentEvents = eventsStore.events ?? []; + if (!Array.isArray(currentEvents)) return []; - for (const event of eventsStore.events) { + const groups: Map = new Map(); + + for (const event of currentEvents) { const start = typeof event.startTime === 'string' ? parseISO(event.startTime) : event.startTime; const dateKey = format(start, 'yyyy-MM-dd'); @@ -132,19 +136,19 @@ .page-header h1 { font-size: 1.75rem; font-weight: 700; - color: hsl(var(--foreground)); + color: hsl(var(--color-foreground)); margin: 0 0 0.25rem 0; } .subtitle { - color: hsl(var(--muted-foreground)); + color: hsl(var(--color-muted-foreground)); margin: 0; } .loading { text-align: center; padding: 2rem; - color: hsl(var(--muted-foreground)); + color: hsl(var(--color-muted-foreground)); } .empty-state { @@ -153,7 +157,7 @@ } .empty-state p { - color: hsl(var(--muted-foreground)); + color: hsl(var(--color-muted-foreground)); margin-bottom: 1rem; } @@ -172,7 +176,7 @@ .date-header { font-size: 0.875rem; font-weight: 600; - color: hsl(var(--muted-foreground)); + color: hsl(var(--color-muted-foreground)); text-transform: uppercase; letter-spacing: 0.05em; margin: 0; @@ -180,7 +184,7 @@ } .date-header.today { - color: hsl(var(--primary)); + color: hsl(var(--color-primary)); } .event-item { @@ -211,13 +215,13 @@ .event-time { font-size: 0.75rem; - color: hsl(var(--muted-foreground)); + color: hsl(var(--color-muted-foreground)); margin-bottom: 0.25rem; } .event-title { font-weight: 500; - color: hsl(var(--foreground)); + color: hsl(var(--color-foreground)); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -225,7 +229,7 @@ .event-location { font-size: 0.875rem; - color: hsl(var(--muted-foreground)); + color: hsl(var(--color-muted-foreground)); margin-top: 0.25rem; } diff --git a/apps/calendar/apps/web/src/routes/calendars/+page.svelte b/apps/calendar/apps/web/src/routes/calendars/+page.svelte index ffcfaae77..16b5294b3 100644 --- a/apps/calendar/apps/web/src/routes/calendars/+page.svelte +++ b/apps/calendar/apps/web/src/routes/calendars/+page.svelte @@ -220,7 +220,7 @@ width: 48px; height: 42px; padding: 4px; - border: 2px solid hsl(var(--border)); + border: 2px solid hsl(var(--color-border)); border-radius: var(--radius-md); cursor: pointer; } @@ -263,9 +263,9 @@ .badge { font-size: 0.75rem; padding: 0.125rem 0.5rem; - background: hsl(var(--muted)); + background: hsl(var(--color-muted)); border-radius: var(--radius-sm); - color: hsl(var(--muted-foreground)); + color: hsl(var(--color-muted-foreground)); } .calendar-actions { @@ -279,12 +279,12 @@ } .text-destructive { - color: hsl(var(--destructive)); + color: hsl(var(--color-error)); } .empty-state { text-align: center; padding: 2rem; - color: hsl(var(--muted-foreground)); + color: hsl(var(--color-muted-foreground)); } diff --git a/apps/calendar/apps/web/src/routes/event/[id]/+page.svelte b/apps/calendar/apps/web/src/routes/event/[id]/+page.svelte index 1125b4bde..ac22ecc3c 100644 --- a/apps/calendar/apps/web/src/routes/event/[id]/+page.svelte +++ b/apps/calendar/apps/web/src/routes/event/[id]/+page.svelte @@ -159,7 +159,7 @@ .page-title { font-size: 1.5rem; font-weight: 600; - color: hsl(var(--foreground)); + color: hsl(var(--color-foreground)); margin: 0; } @@ -171,7 +171,7 @@ .loading { text-align: center; padding: 2rem; - color: hsl(var(--muted-foreground)); + color: hsl(var(--color-muted-foreground)); } .event-details { @@ -189,15 +189,15 @@ .label { font-size: 0.875rem; font-weight: 500; - color: hsl(var(--muted-foreground)); + color: hsl(var(--color-muted-foreground)); } .value { font-size: 1rem; - color: hsl(var(--foreground)); + color: hsl(var(--color-foreground)); } .text-destructive { - color: hsl(var(--destructive)); + color: hsl(var(--color-error)); } diff --git a/apps/calendar/apps/web/src/routes/event/new/+page.svelte b/apps/calendar/apps/web/src/routes/event/new/+page.svelte index bc8f5f7aa..bf7f0e3f5 100644 --- a/apps/calendar/apps/web/src/routes/event/new/+page.svelte +++ b/apps/calendar/apps/web/src/routes/event/new/+page.svelte @@ -68,6 +68,6 @@ font-size: 1.5rem; font-weight: 600; margin-bottom: 1.5rem; - color: hsl(var(--foreground)); + color: hsl(var(--color-foreground)); } diff --git a/apps/calendar/apps/web/src/routes/settings/+page.svelte b/apps/calendar/apps/web/src/routes/settings/+page.svelte index fb2409bd6..7f73eeee1 100644 --- a/apps/calendar/apps/web/src/routes/settings/+page.svelte +++ b/apps/calendar/apps/web/src/routes/settings/+page.svelte @@ -134,12 +134,12 @@ font-weight: 600; margin: 0 0 1rem 0; padding-bottom: 0.75rem; - border-bottom: 1px solid hsl(var(--border)); + border-bottom: 1px solid hsl(var(--color-border)); } .setting-item { padding: 1rem 0; - border-bottom: 1px solid hsl(var(--border) / 0.5); + border-bottom: 1px solid hsl(var(--color-border) / 0.5); } .setting-item:last-child { @@ -156,17 +156,17 @@ .setting-label { font-weight: 500; - color: hsl(var(--foreground)); + color: hsl(var(--color-foreground)); } .setting-description { font-size: 0.875rem; - color: hsl(var(--muted-foreground)); + color: hsl(var(--color-muted-foreground)); } .setting-value { font-size: 0.875rem; - color: hsl(var(--muted-foreground)); + color: hsl(var(--color-muted-foreground)); } .theme-options { @@ -179,22 +179,22 @@ align-items: center; gap: 0.5rem; padding: 0.5rem 1rem; - border: 2px solid hsl(var(--border)); + border: 2px solid hsl(var(--color-border)); border-radius: var(--radius-md); background: transparent; - color: hsl(var(--foreground)); + color: hsl(var(--color-foreground)); font-size: 0.875rem; cursor: pointer; transition: all 150ms ease; } .theme-option:hover { - border-color: hsl(var(--primary) / 0.5); + border-color: hsl(var(--color-primary) / 0.5); } .theme-option.active { - border-color: hsl(var(--primary)); - background: hsl(var(--primary) / 0.1); + border-color: hsl(var(--color-primary)); + background: hsl(var(--color-primary) / 0.1); } .variant-grid { @@ -209,7 +209,7 @@ align-items: center; gap: 0.25rem; padding: 0.75rem; - border: 2px solid hsl(var(--border)); + border: 2px solid hsl(var(--color-border)); border-radius: var(--radius-md); background: transparent; cursor: pointer; @@ -217,12 +217,12 @@ } .variant-option:hover { - border-color: hsl(var(--primary) / 0.5); + border-color: hsl(var(--color-primary) / 0.5); } .variant-option.active { - border-color: hsl(var(--primary)); - background: hsl(var(--primary) / 0.1); + border-color: hsl(var(--color-primary)); + background: hsl(var(--color-primary) / 0.1); } .variant-icon { @@ -231,10 +231,10 @@ .variant-label { font-size: 0.75rem; - color: hsl(var(--muted-foreground)); + color: hsl(var(--color-muted-foreground)); } .text-destructive { - color: hsl(var(--destructive)); + color: hsl(var(--color-error)); } diff --git a/packages/shared-theme-ui/src/components/A11yQuickToggles.svelte b/packages/shared-theme-ui/src/components/A11yQuickToggles.svelte new file mode 100644 index 000000000..df387ddb5 --- /dev/null +++ b/packages/shared-theme-ui/src/components/A11yQuickToggles.svelte @@ -0,0 +1,114 @@ + + +
+ + + + + +
+ + diff --git a/packages/shared-theme-ui/src/components/A11ySettings.svelte b/packages/shared-theme-ui/src/components/A11ySettings.svelte new file mode 100644 index 000000000..b120e039c --- /dev/null +++ b/packages/shared-theme-ui/src/components/A11ySettings.svelte @@ -0,0 +1,188 @@ + + +
+ +
+ {t.contrastLabel} +
+ {#each CONTRAST_OPTIONS as option} + + {/each} +
+
+ + +
+ + +
+ + +
+
+
+ +

{t.reduceMotionDescription}

+
+
+ {#if store.reduceMotionExplicit} + + {/if} + +
+
+
+ + + {#if showReset} +
+ +
+ {/if} +
+ + diff --git a/packages/shared-theme-ui/src/index.ts b/packages/shared-theme-ui/src/index.ts index 932837aa2..546dd0e6d 100644 --- a/packages/shared-theme-ui/src/index.ts +++ b/packages/shared-theme-ui/src/index.ts @@ -3,14 +3,24 @@ export { default as ThemeToggle } from './ThemeToggle.svelte'; export { default as ThemeSelector } from './ThemeSelector.svelte'; export { default as ThemeModeSelector } from './ThemeModeSelector.svelte'; -// New Components +// Theme Components export { default as ThemeColorPreview } from './components/ThemeColorPreview.svelte'; export { default as ThemeCard } from './components/ThemeCard.svelte'; export { default as ThemeGrid } from './components/ThemeGrid.svelte'; +// A11y Components +export { default as A11ySettings } from './components/A11ySettings.svelte'; +export { default as A11yQuickToggles } from './components/A11yQuickToggles.svelte'; + // Pages export { default as ThemePage } from './pages/ThemePage.svelte'; // Types -export type { ThemeStatus, ThemeCardData, ThemePageProps, ThemePageTranslations } from './types'; -export { defaultTranslations } from './types'; +export type { + ThemeStatus, + ThemeCardData, + ThemePageProps, + ThemePageTranslations, + A11yTranslations, +} from './types'; +export { defaultTranslations, defaultA11yTranslations } from './types'; diff --git a/packages/shared-theme-ui/src/pages/ThemePage.svelte b/packages/shared-theme-ui/src/pages/ThemePage.svelte index caca77d11..0b7ec550a 100644 --- a/packages/shared-theme-ui/src/pages/ThemePage.svelte +++ b/packages/shared-theme-ui/src/pages/ThemePage.svelte @@ -1,9 +1,10 @@