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()