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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions .secrets.baseline

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

58 changes: 9 additions & 49 deletions docs/development/DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<style scoped>` to ensure styles don't leak globally
2. **Dark Theme Only**: The application exclusively uses a dark theme
3. **Base Element Styling**: Buttons inherit consistent base styles from `style.css`
4. **Component Structure**: Follow semantic HTML with TypeScript by default
### Theming

Purplex ships a dark theme (default) and a light theme. Every colour, gradient and shadow is a token in `purplex/client/src/styles/tokens.css`: theme-independent tokens (spacing, radius, type) in `:root`, then one dark block and one light block with the same names in the same order. Components only use `var(--token)`; the theme switch (`useTheme`) sets `data-theme` on `<html>`.

- Add a colour token to both theme blocks at the same position; read the rules in the file header.
- No colour literals and no `[data-theme]` selectors outside `tokens.css` (stylelint enforces both).
- Run `yarn theme:check` (in `purplex/client`) before committing: it checks block parity, undefined `var()` names and unused tokens.
- All Vue components use `<style scoped>`; base element styles live in `style.css`.

### Interactive Elements & Animations

Expand All @@ -728,7 +688,7 @@ The project uses a **CSS Variables-based design system** defined in `App.vue` th
### Best Practices

1. **Always use CSS variables** for colors, spacing, and shadows
2. **Maintain dark theme consistency** - no light mode variations
2. **Check both themes** - every visual change must read correctly in dark and light
3. **Use semantic class names** that describe purpose, not appearance
4. **Keep animations subtle** - prioritize usability over flashiness
5. **Test responsive behavior** at all breakpoints
Expand Down
14 changes: 4 additions & 10 deletions docs/reference/TECH_DEBT.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,12 +188,6 @@ Originally Generated: 2024-11-28
- **Status**: Still outstanding
- **Effort**: Medium (2-3 hours)

### 25. Frontend: Dark Mode Inconsistency
- **Location**: Various components
- **Issue**: Some components reference light mode despite dark-mode-only app
- **Status**: Still outstanding
- **Effort**: Low (2-4 hours)

### 26. Docker: No Container Image Signature Verification
- **Location**: `purplex/problems_app/services/docker_execution_service.py`
- **Issue**: Uses tags instead of digests
Expand Down Expand Up @@ -236,13 +230,13 @@ Originally Generated: 2024-11-28
- P1: 7 of 8 (N+1 queries, prefetch fix, Celery retries, GeventPool cleanup, Docker limits, Console.log, Vue API progress)
- P2: 5 of 8 (.extra() usage, database indexes, Redis retry logic)

### Outstanding Items: 11
### Outstanding Items: 10
| Priority | Outstanding | Items |
|----------|-------------|-------|
| P0 | 2 | Component size (ProblemSet/Feedback), Test coverage |
| P1 | 1 | Frontend logging in services |
| P2 | 3 | Analytics caching, Docker blacklist, Hardcoded colors, Animations, Modals |
| P3 | 5 | All P3 items remain |
| P3 | 4 | All P3 items remain |

### Revised Effort Estimate

Expand All @@ -251,7 +245,7 @@ Originally Generated: 2024-11-28
| P0 Critical | 2 | 30-40 |
| P1 High | 1 | 4-6 |
| P2 Medium | 5 | 40-55 |
| P3 Low | 5 | 15-25 |
| **Total** | **13** | **89-126** |
| P3 Low | 4 | 13-21 |
| **Total** | **12** | **87-122** |

*Note: Original estimate was 175-245 hours for 26 issues. Approximately 86-119 hours of work completed.*
27 changes: 15 additions & 12 deletions e2e/visual-theme-audit.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ interface RouteEntry {
auth: AuthRole;
}

// Seeded routes read the fixtures from `python manage.py seed_e2e_data`
// (course CS101-2024 and the e2e-* problem sets), which the E2E stack loads.
const SEEDED_COURSE = 'CS101-2024';

const ROUTES: Record<string, RouteEntry> = {
// Public (no auth required)
'login': { path: '/', auth: null },
Expand All @@ -41,19 +45,18 @@ const ROUTES: Record<string, RouteEntry> = {
'admin-submissions': { path: '/admin/submissions', auth: 'admin' },
'admin-courses': { path: '/admin/courses', auth: 'admin' },
'admin-course-new': { path: '/admin/courses/new', auth: 'admin' },
};

// Data-dependent routes — uncomment after seeding the dev database.
// These require the backend to be running with test data loaded.
//
// 'course-detail': { path: '/courses/ENGGEN131', auth: 'student' },
// 'course-problem-set': { path: '/courses/ENGGEN131/problem-set/demo-problem-set', auth: 'student' },
// 'instructor-course-overview': { path: '/instructor/courses/ENGGEN131', auth: 'instructor' },
// 'instructor-course-students': { path: '/instructor/courses/ENGGEN131/students', auth: 'instructor' },
// 'instructor-course-subs': { path: '/instructor/courses/ENGGEN131/submissions', auth: 'instructor' },
// 'instructor-course-ps': { path: '/instructor/courses/ENGGEN131/problem-sets', auth: 'instructor' },
// 'admin-course-students': { path: '/admin/courses/ENGGEN131/students', auth: 'admin' },
// 'admin-course-ps': { path: '/admin/courses/ENGGEN131/problem-sets', auth: 'admin' },
// Seeded (student, instructor and admin views of CS101-2024)
'course-detail': { path: `/courses/${SEEDED_COURSE}`, auth: 'student' },
'course-ps-mcq': { path: `/courses/${SEEDED_COURSE}/problem-set/e2e-basics?p=0`, auth: 'student' },
'course-ps-probeable': { path: `/courses/${SEEDED_COURSE}/problem-set/e2e-code?p=1`, auth: 'student' },
'course-ps-refute': { path: `/courses/${SEEDED_COURSE}/problem-set/e2e-mixed?p=6`, auth: 'student' },
'instructor-course-overview': { path: `/instructor/courses/${SEEDED_COURSE}`, auth: 'instructor' },
'instructor-course-subs': { path: `/instructor/courses/${SEEDED_COURSE}/submissions`, auth: 'instructor' },
'instructor-course-ps': { path: `/instructor/courses/${SEEDED_COURSE}/problem-sets`, auth: 'instructor' },
'admin-course-students': { path: `/admin/courses/${SEEDED_COURSE}/students`, auth: 'admin' },
'admin-course-ps': { path: `/admin/courses/${SEEDED_COURSE}/problem-sets`, auth: 'admin' },
};

const THEMES = ['dark', 'light'] as const;
const SCREENSHOT_ROOT = path.resolve(__dirname, '..', 'visual-tests', 'screenshots');
Expand Down
14 changes: 11 additions & 3 deletions purplex/client/.stylelintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,15 @@
"rules": {
"color-no-hex": true,
"color-named": "never",
"function-disallowed-list": ["/^rgb/", "/^hsl/"],
"function-disallowed-list": ["/^rgb/", "/^hsl/", "color-mix", "light-dark"],
"selector-class-pattern": null,
"keyframes-name-pattern": null,
"selector-disallowed-list": ["/\\[data-theme/"],
"declaration-property-value-disallowed-list": {
"/^(?!background)/": ["/--color-(primary-)?gradient-/"],
"/^background/": ["/^var\\(--color-(primary-)?gradient-(start|end)\\)$/"],
"/^outline/": ["/--color-accent/"]
},
"scale-unlimited/declaration-strict-value": [
[
"color",
Expand Down Expand Up @@ -40,10 +46,12 @@
"customSyntax": "postcss-html"
},
{
"files": ["src/App.vue", "src/style.css"],
"files": ["src/styles/tokens.css"],
"rules": {
"color-no-hex": null,
"function-disallowed-list": null
"function-disallowed-list": null,
"declaration-property-value-disallowed-list": null,
"selector-disallowed-list": null
}
}
]
Expand Down
3 changes: 2 additions & 1 deletion purplex/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
"i18n:check": "node scripts/check-i18n.mjs",
"i18n:badges": "node scripts/generate-i18n-badges.mjs",
"stylelint": "stylelint \"src/**/*.{vue,css}\" --fix",
"stylelint:check": "stylelint \"src/**/*.{vue,css}\""
"stylelint:check": "stylelint \"src/**/*.{vue,css}\"",
"theme:check": "node scripts/check-theme-tokens.mjs"
},
"dependencies": {
"ace-builds": "^1.32.5",
Expand Down
Loading
Loading