diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cc56d328..63b34d96 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -82,6 +82,10 @@ jobs: yarn lint:check yarn stylelint:check + - name: Check theme tokens + working-directory: purplex/client + run: yarn theme:check + - name: Run TypeScript type checking working-directory: purplex/client continue-on-error: true diff --git a/.secrets.baseline b/.secrets.baseline index 438c1848..ea6d43a3 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -158,21 +158,21 @@ "filename": ".github/workflows/ci.yml", "hashed_secret": "206c80413b9a96c1312cc346b7d2517b84463edd", "is_verified": false, - "line_number": 107 + "line_number": 111 }, { "type": "Basic Auth Credentials", "filename": ".github/workflows/ci.yml", "hashed_secret": "206c80413b9a96c1312cc346b7d2517b84463edd", "is_verified": false, - "line_number": 151 + "line_number": 155 }, { "type": "Secret Keyword", "filename": ".github/workflows/ci.yml", "hashed_secret": "dab4fb93ec0b6fab34ac303369fa5d25cd186a49", "is_verified": false, - "line_number": 153 + "line_number": 157 } ], ".github/workflows/postgres-load-tests.yml": [ @@ -360,5 +360,5 @@ } ] }, - "generated_at": "2026-10-16T22:08:50Z" + "generated_at": "2026-10-17T14:36:50Z" } diff --git a/docs/development/DEVELOPMENT.md b/docs/development/DEVELOPMENT.md index 4c263785..2aefe0a4 100644 --- a/docs/development/DEVELOPMENT.md +++ b/docs/development/DEVELOPMENT.md @@ -664,54 +664,14 @@ Always include relevant context with logs and distinguish between what developer This section documents the established design system, patterns, and conventions used throughout the Purplex frontend. -### CSS Architecture - -The project uses a **CSS Variables-based design system** defined in `App.vue` that provides consistent theming across all components. - -#### Color System -```css -/* Primary Brand Colors */ ---color-primary: #800080; ---color-primary-hover: #9b009b; ---color-primary-gradient-start: #667eea; ---color-primary-gradient-end: #764ba2; - -/* Background Hierarchy */ ---color-bg-main: #242424; /* Main app background */ ---color-bg-dark: #1a1a1a; /* Darker sections */ ---color-bg-panel: #1e1e1e; /* Card/panel backgrounds */ ---color-bg-header: #191919; /* Header backgrounds */ ---color-bg-hover: #2a2a2a; /* Hover states */ - -/* Text Hierarchy */ ---color-text-primary: #ffffff; /* Main text */ ---color-text-secondary: #e0e0e0; /* Secondary text */ ---color-text-muted: #999; /* Muted/disabled text */ - -/* Status Colors */ ---color-success: #4CAF50; ---color-warning: #FFC107; ---color-error: #dc3545; ---color-info: #2196F3; -``` - -#### Spacing System -```css ---spacing-xs: 4px; ---spacing-sm: 8px; ---spacing-md: 12px; ---spacing-base: 15px; ---spacing-lg: 20px; ---spacing-xl: 30px; ---spacing-xxl: 50px; -``` - -### Component Styling Patterns - -1. **Scoped Styles**: All Vue components use ` - - - diff --git a/purplex/client/src/components/AdminUsers.vue b/purplex/client/src/components/AdminUsers.vue index 62c4636f..254ea6d4 100644 --- a/purplex/client/src/components/AdminUsers.vue +++ b/purplex/client/src/components/AdminUsers.vue @@ -249,8 +249,8 @@ onMounted(() => { .search-input:focus { outline: none; - border-color: var(--color-primary-gradient-start); - box-shadow: 0 0 0 2px var(--color-primary-overlay); + border-color: var(--color-focus-ring); + box-shadow: 0 0 0 2px var(--color-accent-overlay); } .filter-select { @@ -294,7 +294,7 @@ onMounted(() => { width: 100%; padding: var(--spacing-sm) var(--spacing-md); background: var(--color-bg-panel); - border: 1px solid var(--color-bg-border); + border: 1px solid var(--color-border); border-radius: var(--radius-base); color: var(--color-text-primary); font-size: var(--font-size-sm); @@ -305,8 +305,8 @@ onMounted(() => { } .role-dropdown:focus { - border-color: var(--color-primary-gradient-start); - box-shadow: 0 0 0 2px var(--color-primary-overlay); + border-color: var(--color-focus-ring); + box-shadow: 0 0 0 2px var(--color-accent-overlay); } .role-dropdown:disabled { @@ -316,7 +316,7 @@ onMounted(() => { } .role-dropdown:hover:not(:disabled) { - border-color: var(--color-primary-gradient-start); + border-color: var(--color-accent); background: var(--color-bg-hover); } diff --git a/purplex/client/src/components/CourseDetail.vue b/purplex/client/src/components/CourseDetail.vue index 5b8b4c31..2731626b 100644 --- a/purplex/client/src/components/CourseDetail.vue +++ b/purplex/client/src/components/CourseDetail.vue @@ -257,7 +257,7 @@ onMounted(async () => { width: 40px; height: 40px; border: 3px solid var(--color-bg-hover); - border-top: 3px solid var(--color-primary-gradient-start); + border-top: 3px solid var(--color-accent); border-radius: var(--radius-circle); animation: spin 1s linear infinite; } @@ -275,7 +275,7 @@ onMounted(async () => { display: inline-flex; align-items: center; gap: var(--spacing-sm); - color: var(--color-primary-gradient-start); + color: var(--color-accent); text-decoration: none; font-weight: 600; margin-bottom: var(--spacing-lg); @@ -283,7 +283,7 @@ onMounted(async () => { } .back-link:hover { - color: var(--color-primary-gradient-end); + color: var(--color-accent-strong); transform: translateX(-2px); } @@ -351,7 +351,7 @@ onMounted(async () => { .problem-set-card { background: var(--color-bg-panel); - border: 2px solid var(--color-bg-border); + border: 2px solid var(--color-border); border-radius: var(--radius-lg); padding: 0; cursor: pointer; @@ -363,7 +363,7 @@ onMounted(async () => { .problem-set-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-lg); - border-color: var(--color-primary-gradient-start); + border-color: var(--color-accent); } .card-content { @@ -474,7 +474,7 @@ onMounted(async () => { background-color: var(--color-bg-panel); border-radius: var(--radius-lg); color: var(--color-text-muted); - border: 2px solid var(--color-bg-border); + border: 2px solid var(--color-border); } /* Error State */ @@ -497,18 +497,18 @@ onMounted(async () => { .home-link { display: inline-block; padding: var(--spacing-md) var(--spacing-xl); - background: linear-gradient(135deg, var(--color-primary-gradient-start) 0%, var(--color-primary-gradient-end) 100%); + background: var(--gradient-primary); color: var(--color-text-on-filled); text-decoration: none; border-radius: var(--radius-base); font-weight: 600; transition: var(--transition-base); - box-shadow: var(--shadow-colored); + box-shadow: var(--shadow-accent); } .home-link:hover { transform: translateY(-2px); - box-shadow: 0 6px 20px var(--color-primary-glow); + box-shadow: 0 6px 20px var(--color-accent-glow); } /* Responsive Design */ diff --git a/purplex/client/src/components/Feedback.vue b/purplex/client/src/components/Feedback.vue index 9fb89631..e95fe771 100644 --- a/purplex/client/src/components/Feedback.vue +++ b/purplex/client/src/components/Feedback.vue @@ -907,7 +907,7 @@ export default defineComponent({ } .attempt-dropdown-trigger:focus { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } @@ -1002,7 +1002,7 @@ export default defineComponent({ } .attempt-item-minimal:focus { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } @@ -1012,14 +1012,14 @@ export default defineComponent({ } .attempt-item-minimal.is-current { - background: var(--color-primary-overlay); + background: var(--color-accent-overlay); color: var(--color-text-primary); } .attempt-indicator { width: 2px; height: 12px; - background: var(--color-bg-border); + background: var(--color-border); border-radius: 1px; position: absolute; left: 2px; @@ -1101,7 +1101,7 @@ export default defineComponent({ } .metric-card:focus { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } @@ -1128,7 +1128,7 @@ export default defineComponent({ /* Progress Bar */ .progress-bar-container { height: 8px; - background: var(--color-bg-dark); + background: var(--color-bg-sunken); border-radius: var(--radius-xs); overflow: hidden; margin-bottom: var(--spacing-md); @@ -1305,7 +1305,7 @@ export default defineComponent({ } .banner-button:focus { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } diff --git a/purplex/client/src/components/HintButton.vue b/purplex/client/src/components/HintButton.vue index 7f830d96..8938ebcb 100644 --- a/purplex/client/src/components/HintButton.vue +++ b/purplex/client/src/components/HintButton.vue @@ -630,12 +630,12 @@ export default { } .hint-button:focus { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } .hint-button:focus-visible { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } @@ -694,7 +694,7 @@ export default { justify-content: space-between; align-items: center; padding: 16px; - border-bottom: 1px solid var(--color-bg-border); + border-bottom: 1px solid var(--color-border); } .hint-menu-header h4 { @@ -723,12 +723,12 @@ export default { } .close-btn:focus { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } .close-btn:focus-visible { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } @@ -762,8 +762,8 @@ export default { } .hint-item.active .hint-header { - background: var(--color-primary-overlay); - border-left: 3px solid var(--color-primary-gradient-start); + background: var(--color-accent-overlay); + border-left: 3px solid var(--color-accent); } .hint-type-icon { @@ -817,7 +817,7 @@ export default { .hint-content { background: var(--color-bg-section); - border-top: 1px solid var(--color-bg-border); + border-top: 1px solid var(--color-border); } .hint-content-body { @@ -845,7 +845,7 @@ export default { .hint-example pre { margin: 0; - background: var(--color-bg-dark); + background: var(--color-bg-sunken); color: var(--color-text-primary); padding: 12px; border-radius: 4px; @@ -856,7 +856,7 @@ export default { .hint-footer { padding: 12px 16px; background: var(--color-bg-section); - border-top: 1px solid var(--color-bg-border); + border-top: 1px solid var(--color-border); display: flex; flex-direction: column; gap: 12px; @@ -890,7 +890,7 @@ export default { } .action-btn.secondary:hover:not(:disabled) { - background: var(--color-bg-border); + background: var(--color-border); } .action-btn.danger { @@ -908,12 +908,12 @@ export default { } .action-btn:focus { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } .action-btn:focus-visible { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } @@ -945,7 +945,7 @@ export default { position: absolute; cursor: pointer; inset: 0; - background-color: var(--color-bg-border); + background-color: var(--color-border); transition: 0.3s; border-radius: 24px; } @@ -964,7 +964,7 @@ export default { } .hint-checkbox:checked + .toggle-slider { - background-color: var(--color-primary-gradient-start); + background-color: var(--color-accent); } .hint-checkbox:checked + .toggle-slider::before { @@ -972,18 +972,18 @@ export default { } .hint-toggle:hover .toggle-slider { - box-shadow: 0 0 0 3px var(--color-primary-overlay); + box-shadow: 0 0 0 3px var(--color-accent-overlay); } .hint-checkbox:focus + .toggle-slider { - box-shadow: 0 0 0 3px var(--color-primary-glow); - outline: 2px solid var(--color-primary-gradient-start); + box-shadow: 0 0 0 3px var(--color-accent-glow); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } .hint-checkbox:focus-visible + .toggle-slider { - box-shadow: 0 0 0 3px var(--color-primary-glow); - outline: 2px solid var(--color-primary-gradient-start); + box-shadow: 0 0 0 3px var(--color-accent-glow); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } diff --git a/purplex/client/src/components/Home.vue b/purplex/client/src/components/Home.vue index 09baade5..fa547226 100644 --- a/purplex/client/src/components/Home.vue +++ b/purplex/client/src/components/Home.vue @@ -320,7 +320,7 @@ main { .problem-set-card { background: var(--color-bg-panel); - border: 2px solid var(--color-bg-border); + border: 2px solid var(--color-border); border-radius: var(--radius-lg); padding: 0; cursor: pointer; @@ -335,16 +335,16 @@ main { .problem-set-card:hover { box-shadow: var(--shadow-lg); - border-color: var(--color-primary-gradient-start); + border-color: var(--color-accent); } .problem-set-card:focus { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } .problem-set-card:focus-visible { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } @@ -443,9 +443,9 @@ main { bottom: 0; left: 0; right: 0; - background: var(--color-primary-glow); + background: var(--color-accent-glow); backdrop-filter: blur(8px); - color: var(--color-primary-gradient-start); + color: var(--color-accent); padding: var(--spacing-lg) var(--spacing-md) var(--spacing-md); text-align: center; font-weight: 600; @@ -531,7 +531,7 @@ main { /* Add Course Button */ .add-course-btn { - background: linear-gradient(135deg, var(--color-primary-gradient-start) 0%, var(--color-primary-gradient-end) 100%); + background: var(--gradient-primary); color: var(--color-text-on-filled); border: none; border-radius: var(--radius-base); @@ -543,12 +543,12 @@ main { display: inline-flex; align-items: center; gap: var(--spacing-sm); - box-shadow: var(--shadow-colored); + box-shadow: var(--shadow-accent); } .add-course-btn:hover { transform: translateY(-2px); - box-shadow: 0 6px 20px var(--color-primary-glow); + box-shadow: 0 6px 20px var(--color-accent-glow); } .add-course-btn:focus { diff --git a/purplex/client/src/components/LanguageSwitcher.vue b/purplex/client/src/components/LanguageSwitcher.vue index 6fa4f2a4..343de931 100644 --- a/purplex/client/src/components/LanguageSwitcher.vue +++ b/purplex/client/src/components/LanguageSwitcher.vue @@ -123,7 +123,7 @@ async function handleLanguageChange() { font-family: inherit; color: var(--color-text-primary); background: var(--color-bg-input); - border: 1px solid var(--color-bg-border); + border: 1px solid var(--color-border); border-radius: var(--radius-base); cursor: pointer; transition: var(--transition-fast); @@ -135,13 +135,13 @@ async function handleLanguageChange() { } .language-select:hover { - border-color: var(--color-primary-gradient-start); + border-color: var(--color-accent); } .language-select:focus { outline: none; - border-color: var(--color-primary-gradient-start); - box-shadow: 0 0 0 2px var(--color-primary-overlay); + border-color: var(--color-focus-ring); + box-shadow: 0 0 0 2px var(--color-accent-overlay); } .language-select:disabled { @@ -150,7 +150,7 @@ async function handleLanguageChange() { } .language-select option { - background: var(--color-bg-dark); + background: var(--color-bg-sunken); color: var(--color-text-primary); padding: var(--spacing-sm); } diff --git a/purplex/client/src/components/NavBar.vue b/purplex/client/src/components/NavBar.vue index 23a140b2..fe917fe6 100644 --- a/purplex/client/src/components/NavBar.vue +++ b/purplex/client/src/components/NavBar.vue @@ -143,7 +143,7 @@ onUnmounted(() => { width: 100%; z-index: 999; background: var(--color-bg-header); - border-bottom: 1px solid var(--color-primary-overlay); + border-bottom: 1px solid var(--color-accent-overlay); box-shadow: var(--shadow-nav); } @@ -179,7 +179,7 @@ onUnmounted(() => { left: 0; width: 0; height: 2px; - background: linear-gradient(90deg, var(--color-primary-gradient-end), var(--color-primary-gradient-start)); + background: linear-gradient(90deg, var(--color-gradient-end), var(--color-gradient-start)); transition: var(--transition-base); } @@ -188,11 +188,11 @@ onUnmounted(() => { } .logo-link:hover { - background: var(--color-primary-overlay); + background: var(--color-accent-overlay); } .logo-link:focus-visible { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } @@ -208,7 +208,7 @@ onUnmounted(() => { font-family: 'Exo 2', sans-serif; font-weight: 800; font-size: 3rem; - background: linear-gradient(135deg, var(--color-logo-start), var(--color-logo-mid), var(--color-logo-end)); + background: var(--gradient-logo); /* stylelint-disable-next-line property-no-vendor-prefix -- needed for Safari */ -webkit-background-clip: text; background-clip: text; @@ -266,10 +266,10 @@ onUnmounted(() => { min-width: 280px; max-width: 340px; background: var(--color-bg-panel); - border: 1px solid var(--color-primary-glow); + border: 1px solid var(--color-accent-glow); border-radius: var(--radius-lg); box-shadow: var(--shadow-dropdown), - 0 0 0 1px var(--color-primary-overlay); + 0 0 0 1px var(--color-accent-overlay); z-index: 1000; animation: panelFadeIn 0.15s ease-out; } @@ -295,8 +295,8 @@ onUnmounted(() => { width: 12px; height: 12px; background: var(--color-bg-panel); - border-left: 1px solid var(--color-primary-glow); - border-top: 1px solid var(--color-primary-glow); + border-left: 1px solid var(--color-accent-glow); + border-top: 1px solid var(--color-accent-glow); transform: rotate(45deg); } @@ -319,7 +319,7 @@ onUnmounted(() => { } .etymology-close:hover { - background: var(--color-primary-overlay); + background: var(--color-accent-overlay); color: var(--color-text-primary); } @@ -356,7 +356,7 @@ onUnmounted(() => { top: 8px; width: 6px; height: 6px; - background: linear-gradient(135deg, var(--color-primary-gradient-start), var(--color-primary-gradient-end)); + background: var(--gradient-primary); border-radius: 50%; } @@ -364,7 +364,7 @@ onUnmounted(() => { .etymology-note { margin: 0; padding-top: var(--spacing-md); - border-top: 1px solid var(--color-primary-overlay); + border-top: 1px solid var(--color-accent-overlay); font-size: var(--font-size-sm); color: var(--color-text-muted); line-height: 1.6; @@ -378,15 +378,15 @@ onUnmounted(() => { gap: var(--spacing-sm); } -/* Individual nav items (both links and buttons) */ +/* Individual nav items (both links and buttons); the Account button uses this base */ .nav-item { display: flex; align-items: center; gap: var(--spacing-xs); padding: var(--spacing-sm) var(--spacing-lg); - background: var(--color-overlay-subtle); + background: var(--color-bg-panel); color: var(--color-text-secondary); - border: 1px solid var(--color-primary-overlay); + border: 1px solid var(--color-border); border-radius: var(--radius-base); font-weight: 600; font-size: var(--font-size-base); @@ -399,13 +399,12 @@ onUnmounted(() => { .nav-item:hover { color: var(--color-text-primary); - background: var(--color-primary-overlay); - border-color: var(--color-primary-glow); - box-shadow: 0 2px 8px var(--color-primary-overlay); + background: var(--color-bg-input); + border-color: var(--color-accent); } .nav-item:focus-visible { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } @@ -414,61 +413,29 @@ onUnmounted(() => { display: inline-block; } -/* Instructor item styling */ -.instructor-item { - background: var(--color-primary); - color: var(--color-text-on-filled); - border-color: transparent; - box-shadow: 0 2px 8px var(--color-info-shadow); -} - -.instructor-item:hover { - background: var(--color-primary-hover); - border-color: transparent; - box-shadow: var(--shadow-info-hover); -} - -.instructor-item.router-link-active { - box-shadow: var(--shadow-info-active); -} - -/* Admin item styling */ +/* Role items (Instructor, Admin): one shared style */ +.instructor-item, .admin-item { - background: var(--color-admin); - color: var(--color-text-on-filled); - border-color: transparent; - box-shadow: 0 2px 8px var(--color-admin-shadow); + background: var(--color-nav-item-bg); + color: var(--color-nav-item-text); + border-color: var(--color-nav-item-border); } +.instructor-item:hover, .admin-item:hover { - background: var(--color-admin-hover); - border-color: transparent; - box-shadow: var(--shadow-admin-hover); -} - -.admin-item.router-link-active { - box-shadow: var(--shadow-admin-active); + background: var(--color-nav-item-hover-bg); + color: var(--color-nav-item-text); + border-color: var(--color-nav-item-border); } -/* Account button styling */ -.account-item { - background: var(--color-bg-dark); - border-color: var(--color-bg-border); +/* Active route (declared after hover, same specificity, so it wins while hovered) */ +.nav-item.router-link-active { + background: var(--color-nav-item-hover-bg); + color: var(--color-accent); + border-color: var(--color-nav-item-border); } -.account-item:hover { - background: var(--color-bg-input); - border-color: var(--color-primary-gradient-start); -} - -/* Active route indicator */ -.router-link-active.nav-item:not(.admin-item) { - background: var(--color-bg-hover); - border-color: var(--color-primary-gradient-start); - color: var(--color-text-primary); -} - -.router-link-active.nav-item:not(.admin-item)::after { +.nav-item.router-link-active::after { content: ''; position: absolute; bottom: 4px; @@ -476,7 +443,7 @@ onUnmounted(() => { transform: translateX(-50%); width: 60%; height: 2px; - background: linear-gradient(90deg, var(--color-primary-gradient-start), var(--color-primary-gradient-end)); + background: var(--color-accent); border-radius: var(--radius-xs); } @@ -553,12 +520,3 @@ onUnmounted(() => { } } - - - diff --git a/purplex/client/src/components/NotificationToast.vue b/purplex/client/src/components/NotificationToast.vue index 03a0c6d0..2ca8d6d1 100644 --- a/purplex/client/src/components/NotificationToast.vue +++ b/purplex/client/src/components/NotificationToast.vue @@ -120,7 +120,7 @@ export default defineComponent({ .notification:hover { transform: translateX(-5px); - box-shadow: var(--shadow-xl); + box-shadow: var(--shadow-lg); } .notification.success { diff --git a/purplex/client/src/components/ThemeSwitcher.vue b/purplex/client/src/components/ThemeSwitcher.vue index 37f254b1..6811128d 100644 --- a/purplex/client/src/components/ThemeSwitcher.vue +++ b/purplex/client/src/components/ThemeSwitcher.vue @@ -84,7 +84,7 @@ const options = computed<{ value: ThemePreference; label: string }[]>(() => [ } .theme-option:focus-visible { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } @@ -95,7 +95,7 @@ const options = computed<{ value: ThemePreference; label: string }[]>(() => [ .theme-option.active { background: var(--color-bg-panel); color: var(--color-text-primary); - box-shadow: var(--shadow-sm), 0 0 0 1px var(--color-bg-border); + box-shadow: var(--shadow-sm), 0 0 0 1px var(--color-border); } @media (width <= 480px) { diff --git a/purplex/client/src/components/activities/feedback/AbstractionCard.vue b/purplex/client/src/components/activities/feedback/AbstractionCard.vue index 2bf10786..ded802e1 100644 --- a/purplex/client/src/components/activities/feedback/AbstractionCard.vue +++ b/purplex/client/src/components/activities/feedback/AbstractionCard.vue @@ -221,7 +221,7 @@ defineExpose({ open }) } .metric-card:focus { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } @@ -253,7 +253,7 @@ defineExpose({ open }) .progress-bar-container { height: 8px; - background: var(--color-bg-dark); + background: var(--color-bg-sunken); border-radius: var(--radius-xs); overflow: hidden; margin-bottom: var(--spacing-md); @@ -274,7 +274,7 @@ defineExpose({ open }) } .progress-bar-fill.bar-locked { - background: var(--color-bg-border); + background: var(--color-border); } .card-status { diff --git a/purplex/client/src/components/activities/feedback/CodeSubmissionFeedback.vue b/purplex/client/src/components/activities/feedback/CodeSubmissionFeedback.vue index e65a91f7..b3c70012 100644 --- a/purplex/client/src/components/activities/feedback/CodeSubmissionFeedback.vue +++ b/purplex/client/src/components/activities/feedback/CodeSubmissionFeedback.vue @@ -638,7 +638,7 @@ onBeforeUnmount(() => { background-size: 200% 100%; border-radius: var(--radius-base); animation: shimmer 2.5s ease-in-out infinite; - border: 1px solid var(--color-bg-border); + border: 1px solid var(--color-border); } .skeleton-test-item { @@ -652,7 +652,7 @@ onBeforeUnmount(() => { background-size: 200% 100%; border-radius: var(--radius-xs); animation: shimmer 2.5s ease-in-out infinite; - border: 1px solid var(--color-bg-border); + border: 1px solid var(--color-border); } @keyframes shimmer { @@ -727,12 +727,12 @@ onBeforeUnmount(() => { } .attempt-dropdown-trigger:focus { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } .attempt-dropdown-trigger:focus-visible { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } @@ -829,12 +829,12 @@ onBeforeUnmount(() => { } .attempt-item-minimal:focus { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } .attempt-item-minimal:focus-visible { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } @@ -848,14 +848,14 @@ onBeforeUnmount(() => { } .attempt-item-minimal.is-current { - background: var(--color-primary-overlay); + background: var(--color-accent-overlay); color: var(--color-text-primary); } .attempt-indicator { width: 2px; height: 12px; - background: var(--color-bg-border); + background: var(--color-border); border-radius: 1px; position: absolute; left: 2px; @@ -991,12 +991,12 @@ onBeforeUnmount(() => { } .test-group-header:focus { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } .test-group-header:focus-visible { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } @@ -1122,12 +1122,12 @@ details:not([open]) .group-icon { } .debug-btn:focus { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } .debug-btn:focus-visible { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } diff --git a/purplex/client/src/components/activities/feedback/McqFeedback.vue b/purplex/client/src/components/activities/feedback/McqFeedback.vue index 9751e719..f1104715 100644 --- a/purplex/client/src/components/activities/feedback/McqFeedback.vue +++ b/purplex/client/src/components/activities/feedback/McqFeedback.vue @@ -173,7 +173,7 @@ const bannerClass = computed(() => { diff --git a/purplex/client/src/components/content/ContentEditorLayout.vue b/purplex/client/src/components/content/ContentEditorLayout.vue index 8f5141cd..a0af73d9 100644 --- a/purplex/client/src/components/content/ContentEditorLayout.vue +++ b/purplex/client/src/components/content/ContentEditorLayout.vue @@ -110,7 +110,7 @@ defineExpose({ ctx }); - - - diff --git a/purplex/client/src/components/instructor/InstructorDashboard.vue b/purplex/client/src/components/instructor/InstructorDashboard.vue index aa46b529..a290dd86 100644 --- a/purplex/client/src/components/instructor/InstructorDashboard.vue +++ b/purplex/client/src/components/instructor/InstructorDashboard.vue @@ -458,7 +458,7 @@ onMounted(() => { .loading-spinner { width: 48px; height: 48px; - border: 4px solid var(--color-bg-border); + border: 4px solid var(--color-border); border-top-color: var(--color-primary); border-radius: 50%; animation: spin 1s linear infinite; @@ -506,7 +506,7 @@ onMounted(() => { } .retry-btn:focus-visible { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } @@ -522,7 +522,7 @@ onMounted(() => { display: flex; align-items: stretch; background: var(--color-bg-panel); - border: 2px solid var(--color-bg-border); + border: 2px solid var(--color-border); border-radius: var(--radius-lg); transition: var(--transition-base); overflow: hidden; @@ -534,12 +534,12 @@ onMounted(() => { .course-card:hover, .course-card:focus-visible { - border-color: var(--color-primary-gradient-start); + border-color: var(--color-focus-ring); box-shadow: var(--shadow-lg); } .course-card:focus-visible { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } @@ -553,9 +553,9 @@ onMounted(() => { align-items: center; justify-content: center; width: 0; - background: var(--color-primary-glow); + background: var(--color-accent-glow); backdrop-filter: blur(8px); - color: var(--color-primary-gradient-start); + color: var(--color-accent); overflow: hidden; transition: width 0.25s ease; } @@ -606,7 +606,7 @@ onMounted(() => { } .course-code { - font-family: var(--font-mono, 'Monaco', 'Menlo', monospace); + font-family: var(--font-mono); background: var(--color-bg-hover); padding: 2px var(--spacing-sm); border-radius: var(--radius-sm); @@ -655,8 +655,8 @@ onMounted(() => { } .role-primary { - background: var(--color-primary-overlay); - color: var(--color-primary-gradient-start); + background: var(--color-accent-overlay); + color: var(--color-accent); } .role-ta { @@ -666,16 +666,16 @@ onMounted(() => { /* Add Course Card - Ghost/Shadow style */ .add-course-card { - border: 2px dashed var(--color-bg-border); + border: 2px dashed var(--color-border); background: transparent; min-height: 80px; } .add-course-card:hover, .add-course-card:focus-visible { - border-color: var(--color-primary-gradient-start); + border-color: var(--color-focus-ring); border-style: solid; - background: var(--color-primary-overlay); + background: var(--color-accent-overlay); } .add-card-content { @@ -694,7 +694,7 @@ onMounted(() => { .add-course-card:hover .add-icon, .add-course-card:focus-visible .add-icon { - color: var(--color-primary-gradient-start); + color: var(--color-accent); } .add-text { @@ -706,13 +706,13 @@ onMounted(() => { .add-course-card:hover .add-text, .add-course-card:focus-visible .add-text { - color: var(--color-primary-gradient-start); + color: var(--color-accent); } /* Expandable Form Card */ .add-course-card.form-expanded { border-style: solid; - border-color: var(--color-primary-gradient-start); + border-color: var(--color-accent); background: var(--color-bg-panel); min-height: auto; cursor: default; @@ -757,7 +757,7 @@ onMounted(() => { align-items: center; margin-bottom: var(--spacing-lg); padding-bottom: var(--spacing-md); - border-bottom: 1px solid var(--color-bg-border); + border-bottom: 1px solid var(--color-border); } .form-header h3 { @@ -812,7 +812,7 @@ onMounted(() => { padding: var(--spacing-md); background: var(--color-bg-input); color: var(--color-text-primary); - border: 2px solid var(--color-bg-border); + border: 2px solid var(--color-border); border-radius: var(--radius-base); font-size: var(--font-size-base); transition: var(--transition-fast); @@ -820,8 +820,8 @@ onMounted(() => { .form-field input:focus { outline: none; - border-color: var(--color-primary-gradient-start); - box-shadow: 0 0 0 3px var(--color-primary-overlay); + border-color: var(--color-focus-ring); + box-shadow: 0 0 0 3px var(--color-accent-overlay); } .form-field input:disabled { @@ -860,7 +860,7 @@ onMounted(() => { color: var(--color-text-muted); font-size: var(--font-size-xs); padding-left: var(--spacing-sm); - border-left: 1px solid var(--color-bg-border); + border-left: 1px solid var(--color-border); } /* Form error */ @@ -880,14 +880,14 @@ onMounted(() => { justify-content: flex-end; margin-top: var(--spacing-lg); padding-top: var(--spacing-md); - border-top: 1px solid var(--color-bg-border); + border-top: 1px solid var(--color-border); } .btn-cancel { padding: var(--spacing-sm) var(--spacing-lg); background: var(--color-bg-hover); color: var(--color-text-secondary); - border: 1px solid var(--color-bg-border); + border: 1px solid var(--color-border); border-radius: var(--radius-base); font-weight: 600; cursor: pointer; @@ -901,14 +901,14 @@ onMounted(() => { .btn-create { padding: var(--spacing-sm) var(--spacing-xl); - background: linear-gradient(135deg, var(--color-admin) 0%, var(--color-primary-gradient-end) 100%); + background: var(--gradient-admin); color: var(--color-text-on-filled); border: none; border-radius: var(--radius-base); font-weight: 600; cursor: pointer; transition: var(--transition-base); - box-shadow: var(--shadow-colored); + box-shadow: var(--shadow-accent); } .btn-create:disabled { @@ -920,7 +920,7 @@ onMounted(() => { .btn-create:hover:not(:disabled) { transform: translateY(-2px); - box-shadow: 0 6px 20px var(--color-primary-glow); + box-shadow: 0 6px 20px var(--color-accent-glow); } /* Mobile responsive */ diff --git a/purplex/client/src/components/instructor/InstructorNavBar.vue b/purplex/client/src/components/instructor/InstructorNavBar.vue index 8ea15279..94bb75c7 100644 --- a/purplex/client/src/components/instructor/InstructorNavBar.vue +++ b/purplex/client/src/components/instructor/InstructorNavBar.vue @@ -184,7 +184,7 @@ const isActiveRoute = (path: string): boolean => { position: fixed; top: -100%; left: 0; - background: var(--color-primary-gradient-start); + background: var(--color-accent); color: var(--color-text-on-filled); padding: var(--spacing-md) var(--spacing-lg); text-decoration: none; @@ -202,8 +202,8 @@ const isActiveRoute = (path: string): boolean => { } .instructor-nav { - background: linear-gradient(135deg, var(--color-admin) 0%, var(--color-admin-hover) 100%); - color: var(--color-text-on-filled); + background: var(--nav-sub-bg); + color: var(--color-nav-sub-text); width: 100%; padding: var(--spacing-lg) 0; margin-bottom: var(--spacing-xl); @@ -266,7 +266,7 @@ const isActiveRoute = (path: string): boolean => { .instructor-link { display: flex; align-items: center; - color: var(--color-text-primary); + color: var(--color-nav-sub-text); text-decoration: none; padding: var(--spacing-md) var(--spacing-xl); border-radius: var(--radius-base); @@ -295,17 +295,18 @@ const isActiveRoute = (path: string): boolean => { .instructor-link:hover { transform: translateY(-2px); - border-color: var(--color-overlay-strong); + border-color: var(--color-nav-sub-hover-border); } .instructor-link:focus-visible { - outline: 2px solid var(--color-focus-ring); + outline: 2px solid var(--color-nav-sub-focus); outline-offset: 2px; } .instructor-link.router-link-active { - background: var(--color-overlay-strong); - border-color: var(--color-overlay-strong); + background: var(--color-nav-sub-active-bg); + border-color: var(--color-nav-sub-active-bg); + color: var(--color-nav-sub-active-text); box-shadow: var(--shadow-base), 0 0 20px var(--color-overlay-strong); @@ -318,7 +319,7 @@ const isActiveRoute = (path: string): boolean => { left: 20%; right: 20%; height: 4px; - background: var(--color-text-primary); + background: var(--color-nav-sub-active-text); border-radius: var(--radius-xs); box-shadow: 0 0 8px var(--color-overlay-strong); } @@ -372,34 +373,3 @@ const isActiveRoute = (path: string): boolean => { } } - - - diff --git a/purplex/client/src/components/instructor/InstructorStudents.vue b/purplex/client/src/components/instructor/InstructorStudents.vue index 479ec520..62664474 100644 --- a/purplex/client/src/components/instructor/InstructorStudents.vue +++ b/purplex/client/src/components/instructor/InstructorStudents.vue @@ -298,7 +298,7 @@ onMounted(() => { .instructor-students { min-height: 100vh; - background: var(--color-background); + background: var(--color-bg-main); } .content-container { @@ -324,7 +324,7 @@ onMounted(() => { width: 48px; height: 48px; border: 4px solid var(--color-bg-input); - border-top-color: var(--color-primary-gradient-start); + border-top-color: var(--color-accent); border-radius: 50%; animation: spin 1s linear infinite; margin-bottom: var(--spacing-lg); @@ -375,7 +375,7 @@ onMounted(() => { } .retry-btn:focus-visible { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } @@ -405,8 +405,8 @@ onMounted(() => { .search-input:focus { outline: none; - border-color: var(--color-primary-gradient-start); - box-shadow: 0 0 0 3px var(--color-primary-overlay); + border-color: var(--color-focus-ring); + box-shadow: 0 0 0 3px var(--color-accent-overlay); } .search-input::placeholder { diff --git a/purplex/client/src/components/privacy/AIConsentModal.vue b/purplex/client/src/components/privacy/AIConsentModal.vue index b97e7990..2ac8fd30 100644 --- a/purplex/client/src/components/privacy/AIConsentModal.vue +++ b/purplex/client/src/components/privacy/AIConsentModal.vue @@ -143,7 +143,7 @@ function onDecline() { .btn-secondary { background: var(--color-bg-hover); color: var(--color-text-secondary); - border: 1px solid var(--color-bg-border); + border: 1px solid var(--color-border); } .btn-primary { diff --git a/purplex/client/src/components/privacy/AccountDeletion.vue b/purplex/client/src/components/privacy/AccountDeletion.vue index d76d81c0..eaa31af8 100644 --- a/purplex/client/src/components/privacy/AccountDeletion.vue +++ b/purplex/client/src/components/privacy/AccountDeletion.vue @@ -142,7 +142,7 @@ function formatDate(isoDate: string): string { padding: var(--spacing-xl); background: var(--color-bg-panel); border-radius: var(--radius-base); - border: 1px solid var(--color-bg-border); + border: 1px solid var(--color-border); box-shadow: var(--shadow-sm); } @@ -203,7 +203,7 @@ function formatDate(isoDate: string): string { } .account-deletion__btn:focus-visible { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } @@ -218,13 +218,13 @@ function formatDate(isoDate: string): string { } .account-deletion__btn--cancel { - background: linear-gradient(135deg, var(--color-primary-gradient-start), var(--color-primary-gradient-end)); + background: var(--gradient-primary); color: var(--color-text-on-filled); } .account-deletion__btn--cancel:hover:not(:disabled) { transform: translateY(-2px); - box-shadow: var(--shadow-colored); + box-shadow: var(--shadow-accent); } .account-deletion__pending { diff --git a/purplex/client/src/components/privacy/AgeGate.vue b/purplex/client/src/components/privacy/AgeGate.vue index cd93d00e..40f3eb0e 100644 --- a/purplex/client/src/components/privacy/AgeGate.vue +++ b/purplex/client/src/components/privacy/AgeGate.vue @@ -115,7 +115,7 @@ function submitAge() { padding: var(--spacing-xl); background: var(--color-bg-panel); border-radius: var(--radius-base); - border: 1px solid var(--color-bg-border); + border: 1px solid var(--color-border); box-shadow: var(--shadow-base); } @@ -147,7 +147,7 @@ function submitAge() { .age-gate__input { padding: var(--spacing-sm) var(--spacing-md); - border: 1px solid var(--color-bg-border); + border: 1px solid var(--color-border); border-radius: var(--radius-xs); background: var(--color-bg-input); color: var(--color-text-primary); @@ -156,7 +156,7 @@ function submitAge() { .age-gate__input:focus { outline: none; - border-color: var(--color-primary-gradient-start); + border-color: var(--color-focus-ring); } .age-gate__message { @@ -180,7 +180,7 @@ function submitAge() { .age-gate__submit { padding: var(--spacing-md); - background: linear-gradient(135deg, var(--color-primary-gradient-start), var(--color-primary-gradient-end)); + background: var(--gradient-primary); color: var(--color-text-on-filled); border: none; border-radius: var(--radius-sm); @@ -191,7 +191,7 @@ function submitAge() { } .age-gate__submit:focus-visible { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } @@ -202,6 +202,6 @@ function submitAge() { .age-gate__submit:hover:not(:disabled) { transform: translateY(-2px); - box-shadow: var(--shadow-colored); + box-shadow: var(--shadow-accent); } diff --git a/purplex/client/src/components/privacy/ConsentForm.vue b/purplex/client/src/components/privacy/ConsentForm.vue index 495c22ed..58941209 100644 --- a/purplex/client/src/components/privacy/ConsentForm.vue +++ b/purplex/client/src/components/privacy/ConsentForm.vue @@ -145,7 +145,7 @@ function submitConsent() { padding: var(--spacing-xl); background: var(--color-bg-panel); border-radius: var(--radius-base); - border: 1px solid var(--color-bg-border); + border: 1px solid var(--color-border); box-shadow: var(--shadow-base); } @@ -171,7 +171,7 @@ function submitConsent() { .consent-form__optional-header { margin-top: var(--spacing-sm); padding-top: var(--spacing-md); - border-top: 1px solid var(--color-bg-border); + border-top: 1px solid var(--color-border); font-size: var(--font-size-xs); color: var(--color-text-muted); } @@ -188,7 +188,7 @@ function submitConsent() { .consent-item input[type="checkbox"] { margin-top: 3px; flex-shrink: 0; - accent-color: var(--color-primary-gradient-start); + accent-color: var(--color-accent); } .consent-item__text { @@ -198,11 +198,11 @@ function submitConsent() { } .consent-item__text a { - color: var(--color-primary-gradient-start); + color: var(--color-accent); } .consent-item__text a:hover { - color: var(--color-primary-gradient-end); + color: var(--color-accent-strong); } .consent-item__detail { @@ -219,7 +219,7 @@ function submitConsent() { .consent-form__submit { width: 100%; padding: var(--spacing-md); - background: linear-gradient(135deg, var(--color-primary-gradient-start), var(--color-primary-gradient-end)); + background: var(--gradient-primary); color: var(--color-text-on-filled); border: none; border-radius: var(--radius-sm); @@ -230,7 +230,7 @@ function submitConsent() { } .consent-form__submit:focus-visible { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } @@ -241,6 +241,6 @@ function submitConsent() { .consent-form__submit:hover:not(:disabled) { transform: translateY(-2px); - box-shadow: var(--shadow-colored); + box-shadow: var(--shadow-accent); } diff --git a/purplex/client/src/components/privacy/CookieConsent.vue b/purplex/client/src/components/privacy/CookieConsent.vue index c96f8420..a8f17f4e 100644 --- a/purplex/client/src/components/privacy/CookieConsent.vue +++ b/purplex/client/src/components/privacy/CookieConsent.vue @@ -75,7 +75,7 @@ function acceptAll() { right: 0; z-index: 9999; background: var(--color-bg-header); - border-top: 1px solid var(--color-bg-border); + border-top: 1px solid var(--color-border); padding: var(--spacing-base) var(--spacing-xl); box-shadow: var(--shadow-up); } @@ -99,11 +99,11 @@ function acceptAll() { } .cookie-consent__link { - color: var(--color-primary-gradient-start); + color: var(--color-accent); } .cookie-consent__link:hover { - color: var(--color-primary-gradient-end); + color: var(--color-accent-strong); } .cookie-consent__actions { @@ -118,12 +118,12 @@ function acceptAll() { font-size: var(--font-size-sm); font-weight: 500; cursor: pointer; - border: 1px solid var(--color-bg-border); + border: 1px solid var(--color-border); transition: var(--transition-fast); } .cookie-consent__btn:focus-visible { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } @@ -134,18 +134,18 @@ function acceptAll() { .cookie-consent__btn--essential:hover { background: var(--color-bg-hover); - border-color: var(--color-primary-gradient-start); + border-color: var(--color-accent); } .cookie-consent__btn--accept { - background: linear-gradient(135deg, var(--color-primary-gradient-start), var(--color-primary-gradient-end)); + background: var(--gradient-primary); color: var(--color-text-on-filled); border-color: transparent; } .cookie-consent__btn--accept:hover { transform: translateY(-2px); - box-shadow: var(--shadow-colored); + box-shadow: var(--shadow-accent); } @media (width <= 768px) { diff --git a/purplex/client/src/components/privacy/DataExport.vue b/purplex/client/src/components/privacy/DataExport.vue index 3ad2c0ef..674b9e3c 100644 --- a/purplex/client/src/components/privacy/DataExport.vue +++ b/purplex/client/src/components/privacy/DataExport.vue @@ -66,7 +66,7 @@ async function exportData() { padding: var(--spacing-xl); background: var(--color-bg-panel); border-radius: var(--radius-base); - border: 1px solid var(--color-bg-border); + border: 1px solid var(--color-border); box-shadow: var(--shadow-sm); } @@ -84,7 +84,7 @@ async function exportData() { .data-export__btn { padding: var(--spacing-sm) var(--spacing-lg); - background: linear-gradient(135deg, var(--color-primary-gradient-start), var(--color-primary-gradient-end)); + background: var(--gradient-primary); color: var(--color-text-on-filled); border: none; border-radius: var(--radius-sm); @@ -94,7 +94,7 @@ async function exportData() { } .data-export__btn:focus-visible { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } @@ -105,7 +105,7 @@ async function exportData() { .data-export__btn:hover:not(:disabled) { transform: translateY(-2px); - box-shadow: var(--shadow-colored); + box-shadow: var(--shadow-accent); } .data-export__error { diff --git a/purplex/client/src/components/privacy/PrivacyPolicy.vue b/purplex/client/src/components/privacy/PrivacyPolicy.vue index 5321769b..7be49e47 100644 --- a/purplex/client/src/components/privacy/PrivacyPolicy.vue +++ b/purplex/client/src/components/privacy/PrivacyPolicy.vue @@ -156,10 +156,10 @@ const effectiveDate = new Date().toLocaleDateString(undefined, { } .policy-page a { - color: var(--color-primary-gradient-start); + color: var(--color-accent); } .policy-page a:hover { - color: var(--color-primary-gradient-end); + color: var(--color-accent-strong); } diff --git a/purplex/client/src/components/privacy/PrivacySettings.vue b/purplex/client/src/components/privacy/PrivacySettings.vue index e124d09f..1ecd6f4a 100644 --- a/purplex/client/src/components/privacy/PrivacySettings.vue +++ b/purplex/client/src/components/privacy/PrivacySettings.vue @@ -204,7 +204,7 @@ async function updateDirectoryInfo() { padding: var(--spacing-md) var(--spacing-base); background: var(--color-bg-panel); border-radius: var(--radius-sm); - border: 1px solid var(--color-bg-border); + border: 1px solid var(--color-border); transition: var(--transition-fast); } @@ -226,8 +226,8 @@ async function updateDirectoryInfo() { .consent-toggle__required { font-size: var(--font-size-xs); padding: 2px var(--spacing-xs); - background: var(--color-primary-overlay); - color: var(--color-primary-gradient-start); + background: var(--color-accent-overlay); + color: var(--color-accent); border-radius: var(--radius-xs); font-weight: 600; } @@ -249,7 +249,7 @@ async function updateDirectoryInfo() { .consent-toggle__slider { position: absolute; inset: 0; - background: var(--color-bg-border); + background: var(--color-border); border-radius: var(--radius-round); transition: var(--transition-fast); } @@ -267,7 +267,7 @@ async function updateDirectoryInfo() { } .consent-toggle__switch input:checked + .consent-toggle__slider { - background: var(--color-primary-gradient-start); + background: var(--color-accent); } .consent-toggle__switch input:checked + .consent-toggle__slider::before { @@ -280,7 +280,7 @@ async function updateDirectoryInfo() { } .consent-toggle__switch input:focus-visible + .consent-toggle__slider { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } diff --git a/purplex/client/src/components/privacy/TermsOfService.vue b/purplex/client/src/components/privacy/TermsOfService.vue index e8b1d8df..4a80f272 100644 --- a/purplex/client/src/components/privacy/TermsOfService.vue +++ b/purplex/client/src/components/privacy/TermsOfService.vue @@ -134,10 +134,10 @@ const effectiveDate = new Date().toLocaleDateString(undefined, { } .policy-page a { - color: var(--color-primary-gradient-start); + color: var(--color-accent); } .policy-page a:hover { - color: var(--color-primary-gradient-end); + color: var(--color-accent-strong); } diff --git a/purplex/client/src/components/segmentation/SegmentAnalysisModal.vue b/purplex/client/src/components/segmentation/SegmentAnalysisModal.vue index d8038bda..08799255 100644 --- a/purplex/client/src/components/segmentation/SegmentAnalysisModal.vue +++ b/purplex/client/src/components/segmentation/SegmentAnalysisModal.vue @@ -369,7 +369,7 @@ onMounted(() => { } .badge-count { - background: var(--color-bg-border); + background: var(--color-border); color: var(--color-text-muted); } @@ -414,7 +414,7 @@ onMounted(() => { .size-controls { display: flex; gap: var(--spacing-xs); - background: var(--color-bg-dark); + background: var(--color-bg-sunken); padding: var(--spacing-xs); border-radius: var(--radius-sm); } @@ -476,7 +476,7 @@ onMounted(() => { .analysis-header { padding: var(--spacing-lg); background: var(--color-bg-hover); - border-bottom: 1px solid var(--color-bg-border); + border-bottom: 1px solid var(--color-border); } .feedback-content { @@ -527,9 +527,9 @@ onMounted(() => { } .segment-badge-goal { - background: var(--color-admin); - color: var(--color-text-on-filled); - box-shadow: none; + background: var(--color-info-bg); + color: var(--color-accent); + box-shadow: inset 0 0 0 1px var(--color-accent); } .feedback-explanation { diff --git a/purplex/client/src/components/segmentation/SegmentMapping.vue b/purplex/client/src/components/segmentation/SegmentMapping.vue index d8b1a064..4e82fa84 100644 --- a/purplex/client/src/components/segmentation/SegmentMapping.vue +++ b/purplex/client/src/components/segmentation/SegmentMapping.vue @@ -151,8 +151,7 @@ export default defineComponent({ activeSegment: null as number | null, selectedSegment: null as number | null, canvasWidth: 200 as number, - canvasHeight: 400 as number, - connectionColor: '#667eea' as string + canvasHeight: 400 as number }; }, computed: { @@ -295,7 +294,7 @@ export default defineComponent({ // Read segment color from CSS variables (theme-aware) const index = ((segmentId - 1) % 6) + 1; return getComputedStyle(document.documentElement) - .getPropertyValue(`--color-segment-${index}`).trim() || '#9f7aea'; + .getPropertyValue(`--color-category-${index}`).trim() || `var(--color-category-${index})`; }, getSegmentNumber(segmentId: number): number { @@ -417,7 +416,7 @@ export default defineComponent({ padding: var(--spacing-md); background: var(--color-bg-panel); border-radius: var(--radius-sm); - border: 1px solid var(--color-bg-border); + border: 1px solid var(--color-border); margin-top: var(--spacing-sm); } @@ -472,9 +471,9 @@ export default defineComponent({ font-family: Monaco, Menlo, 'Ubuntu Mono', monospace; font-size: var(--font-size-sm); line-height: 1.4; - background: var(--color-bg-dark); + background: var(--color-bg-sunken); border-radius: var(--radius-sm); - border: 1px solid var(--color-bg-border); + border: 1px solid var(--color-border); overflow-x: auto; } @@ -498,7 +497,7 @@ export default defineComponent({ flex-shrink: 0; background: var(--color-bg-section); padding: 2px 8px 2px 4px; - border-right: 1px solid var(--color-bg-border); + border-right: 1px solid var(--color-border); border-left: 3px solid transparent; margin-right: 12px; font-weight: normal; @@ -528,15 +527,15 @@ export default defineComponent({ } .code-line.highlighted { - background: var(--color-primary-overlay); + background: var(--color-accent-overlay); } .code-line.highlighted .line-number { - background: var(--color-primary-overlay); + background: var(--color-accent-overlay); } .code-line.highlighted .line-content { - background: var(--color-primary-overlay); + background: var(--color-accent-overlay); } .code-line.dimmed { @@ -594,7 +593,7 @@ export default defineComponent({ .response-panel::-webkit-scrollbar-thumb, .code-panel::-webkit-scrollbar-thumb { - background: var(--color-bg-border); + background: var(--color-border); border-radius: 2px; } diff --git a/purplex/client/src/components/ui/AsyncError.vue b/purplex/client/src/components/ui/AsyncError.vue index 4d316a84..7a7dac80 100644 --- a/purplex/client/src/components/ui/AsyncError.vue +++ b/purplex/client/src/components/ui/AsyncError.vue @@ -54,7 +54,7 @@ defineProps<{ .retry-button { padding: var(--spacing-sm) var(--spacing-md); background: var(--color-bg-hover); - border: 1px solid var(--color-bg-border); + border: 1px solid var(--color-border); border-radius: var(--radius-base); color: var(--color-text-secondary); font-size: var(--font-size-sm); @@ -64,12 +64,12 @@ defineProps<{ .retry-button:hover { background: var(--color-bg-input); - border-color: var(--color-primary-gradient-start); + border-color: var(--color-accent); color: var(--color-text-primary); } .retry-button:focus { - outline: 2px solid var(--color-primary-gradient-start); + outline: 2px solid var(--color-focus-ring); outline-offset: 2px; } diff --git a/purplex/client/src/components/ui/AsyncLoader.vue b/purplex/client/src/components/ui/AsyncLoader.vue index acba8612..81b6feca 100644 --- a/purplex/client/src/components/ui/AsyncLoader.vue +++ b/purplex/client/src/components/ui/AsyncLoader.vue @@ -30,7 +30,7 @@ width: 32px; height: 32px; border: 3px solid var(--color-bg-input); - border-top-color: var(--color-primary-gradient-start); + border-top-color: var(--color-accent); border-radius: var(--radius-circle); animation: spin 0.8s linear infinite; } diff --git a/purplex/client/src/components/ui/ConfirmDialog.vue b/purplex/client/src/components/ui/ConfirmDialog.vue index 23811686..8593da14 100644 --- a/purplex/client/src/components/ui/ConfirmDialog.vue +++ b/purplex/client/src/components/ui/ConfirmDialog.vue @@ -112,7 +112,7 @@ defineEmits<{ .btn-secondary { background: var(--color-bg-hover); color: var(--color-text-secondary); - border: 1px solid var(--color-bg-border); + border: 1px solid var(--color-border); } .btn-danger { diff --git a/purplex/client/src/components/ui/DataTable.vue b/purplex/client/src/components/ui/DataTable.vue index 512eb2ad..a1db7724 100644 --- a/purplex/client/src/components/ui/DataTable.vue +++ b/purplex/client/src/components/ui/DataTable.vue @@ -302,7 +302,7 @@ function getRowKey(item: T, index: number): string | number { .retry-button { padding: var(--spacing-sm) var(--spacing-lg); - background: linear-gradient(135deg, var(--color-primary-gradient-start) 0%, var(--color-primary-gradient-end) 100%); + background: var(--gradient-primary); border: none; border-radius: var(--radius-base); color: var(--color-text-on-filled); @@ -313,7 +313,7 @@ function getRowKey(item: T, index: number): string | number { .retry-button:hover { transform: translateY(-2px); - box-shadow: var(--shadow-colored); + box-shadow: var(--shadow-accent); } .table-wrapper { @@ -369,7 +369,7 @@ function getRowKey(item: T, index: number): string | number { text-align: center; padding: var(--spacing-xxl); background: var(--color-bg-panel); - border: 1px dashed var(--color-border, var(--color-bg-input)); + border: 1px dashed var(--color-bg-input); border-radius: var(--radius-lg); } diff --git a/purplex/client/src/components/ui/DataTablePagination.vue b/purplex/client/src/components/ui/DataTablePagination.vue index d4e73ef6..59feb40d 100644 --- a/purplex/client/src/components/ui/DataTablePagination.vue +++ b/purplex/client/src/components/ui/DataTablePagination.vue @@ -116,7 +116,7 @@ function handlePageSizeChange(event: Event) { justify-content: space-between; align-items: center; padding: var(--spacing-md) var(--spacing-lg); - border-top: 1px solid var(--color-border, var(--color-bg-input)); + border-top: 1px solid var(--color-bg-input); } .pagination-left { @@ -151,9 +151,9 @@ function handlePageSizeChange(event: Event) { .page-size-select { padding: var(--spacing-xs) var(--spacing-sm); - border: 1px solid var(--color-border, var(--color-bg-input)); + border: 1px solid var(--color-bg-input); border-radius: var(--radius-base); - background: var(--color-surface, var(--color-bg-hover)); + background: var(--color-bg-hover); color: var(--color-text-primary); font-size: var(--font-size-sm); cursor: pointer; @@ -182,17 +182,17 @@ function handlePageSizeChange(event: Event) { } .pagination-btn:hover:not(:disabled) { - border-color: var(--color-primary-gradient-start); - background: var(--color-primary-gradient-start); + border-color: var(--color-accent); + background: var(--color-accent); color: var(--color-text-on-filled); } .pagination-btn.active { - background: linear-gradient(135deg, var(--color-primary-gradient-start) 0%, var(--color-primary-gradient-end) 100%); - border-color: var(--color-primary-gradient-start); + background: var(--gradient-primary); + border-color: var(--color-accent); color: var(--color-text-on-filled); font-weight: 600; - box-shadow: var(--shadow-colored); + box-shadow: var(--shadow-accent); } /* Responsive */ diff --git a/purplex/client/src/components/ui/FunctionCallTable.vue b/purplex/client/src/components/ui/FunctionCallTable.vue index 08da2133..3d8ff2db 100644 --- a/purplex/client/src/components/ui/FunctionCallTable.vue +++ b/purplex/client/src/components/ui/FunctionCallTable.vue @@ -74,7 +74,7 @@ function parseParamNames(signature: string): string[] { diff --git a/purplex/client/src/components/ui/TerminalDisplay.vue b/purplex/client/src/components/ui/TerminalDisplay.vue index 05fad05a..22715e8e 100644 --- a/purplex/client/src/components/ui/TerminalDisplay.vue +++ b/purplex/client/src/components/ui/TerminalDisplay.vue @@ -52,7 +52,7 @@ defineProps()