This document outlines the details, development guidelines, and setup for the VueJS-based frontend for BW-Lehrpool.
Quick Links:
- Active Repository: Khoding/bwlp-vue-frontend
- Archived Repository (for reference): Khoding/bwlp-frontend
- Core Framework: Vue.js (v3.5)
- Build Tool: Vite
- CSS Framework: Beer CSS
- A CSS library based on Material Design 3 (M3)
- Facilitates rapid Material Design implementation.
- Supports Material You theming (dynamic color theming, e.g., based on images)
- Read Helpers documentation on GitHub.
- Icons: Material Symbols via Google Fonts
- Visual Studio Code (VSCode)
- Essential Extensions:
npm install- Copy and paste the
.env.*.examplefiles and remove the.example copyfrom the name.
- Development (with Hot-Reload):
npm run dev
- Production Build (Compile and Minify):
npm run build
npm run devrunsvite, main command (takes information from.env.development)npm run buildrunsvite build, self explanatorynpm run previewrunsvite preview, to view the website as it is in production (takes information from.env.production)npx vite --port=4000(the same asrun dev)
Add these snippets to your VS Code user snippets for the vue language to speed up development:
{
"Vue Component": {
"prefix": "vue-component",
"body": [
"<template>",
" $1",
"</template>",
"",
"<script setup>",
" $2",
"</script>",
"",
"<style scoped>",
" $3",
"</style>"
],
"description": "Create a Vue component"
},
"Vue Component TypeScript": {
"prefix": "vue-component-ts",
"body": [
"<template>",
" $1",
"</template>",
"",
"<script setup lang=\"ts\">",
" $2",
"</script>",
"",
"<style scoped>",
" $3",
"</style>"
],
"description": "Create a Vue component with TypeScript"
},
"Vue Method": {
"prefix": "vue-method",
"body": ["${1:methodName}() {", " ${2:// method body}", "}"],
"description": "Create a Vue method"
},
"Vue Computed Property": {
"prefix": "vue-computed",
"body": ["computed: {", " ${1:computedProperty}() {", " return ${2:value};", " }", "}"],
"description": "Create a Vue computed property"
},
"Vue Watcher": {
"prefix": "vue-watch",
"body": [
"watch: {",
" ${1:propertyToWatch}(newValue, oldValue) {",
" ${2:// watcher body}",
" }",
"}"
],
"description": "Create a Vue watcher"
},
"Vue Script Setup": {
"prefix": "setup",
"body": ["<script setup>", " $0", "</script>"],
"description": "Create a vue script setup block"
},
"Vue Script Setup TypeScript": {
"prefix": "setup",
"body": ["<script setup lang=\"ts\">", " $0", "</script>"],
"description": "Create a vue script setup block with TypeScript"
},
"Vue Scoped": {
"prefix": "scoped",
"body": ["<style scoped>", " $0", "</style>"],
"description": "Create a scoped style block"
}
}For advanced configuration options, refer to the Vite Configuration Reference.
A strong emphasis is placed on code cleanliness and maintainability.
Reference: Vue SFC Specification
Philosophy: Components should be extracted frequently.
- Main Reason: A component represents a distinct piece of the UI, regardless of its size or reuse frequency. Extracting makes the structure logical and mirrors the UI breakdown. This promotes code sharing via composables or utils. While it can mean navigating more files, modern IDEs facilitate this.
- Example: The
DetailDialogcomponent was initially part of a page, then extracted. Its internal tabs were later extracted into their own components, resulting in a clear structure (@/components/dialog/) where related files reside.
- Example: The
- Technical Reason: Improves readability of parent components/views. Well-named components (
SortableTable) make the template understandable without needing to dive into their implementation immediately.
SFC Block Order: Use template -> script -> style.
<template></template>: Defines the component's structure. Comes first as it's often the primary block needed to understand the component's purpose.<script setup lang="ts"></script>: (Optional) Contains the component's logic.- Must use: Composition API via
<script setup>. It's the modern standard, enables tools like VueUse, and removes limitations of the Options API. - Attribute Order:
setupfirst (defines API), thenlang="ts"(defines language). - Omit the
<script>block if the component has no logic (common for simple presentational components).
- Must use: Composition API via
<style scoped></style>: (Optional) Contains component-specific styles.- Comes last as it has the least impact on understanding the component's core function.
- Scoped by Default: Use
scopedin 99.9% of cases to prevent styles from leaking. - Global Styles: Place genuinely global styles in the main CSS file (
@/assets/main.cssor similar). - Targeting Child/Slot Content: Learn and use
:deep()and:slotted()selectors (SFC CSS Features). - Escaping Scope: If
scoped,:deep, and:slottedfail for a specific rule that clearly belongs to the component, use:global(.specific-class-name)for that rule only. Do not make the entire<style>block global. - Use Classes: Scoped styles don't eliminate the need for classes. Classes improve performance and readability (Scoped Style Tips).
- Omit the
<style>block if the component has no custom styles.
Rationale for template-script-style order:
- Aligns with official Vue documentation examples.
- Semantic flow: Structure (
template) -> Logic (script) -> Appearance (style). - Developer preference: Often, reading the
templatefirst is sufficient to grasp the component's role. Scrolling down to thescript(which can sometimes be long) is only necessary for deeper understanding.
- Props & Events Naming:
- Define in
scriptusingcamelCase. - Use in
templateusingkebab-case. - Example: Define
defineProps({ myProp: String }), use<MyComponent :my-prop="value" />.
- Define in
- Component Imports:
- Always use the alias path
@/(e.g.,import MyComponent from '@/components/MyComponent.vue').@/is an alias for./src.
- Avoid relative paths (
../or./) for better clarity on file location.
- Always use the alias path
- Code Extraction:
- If logic within an SFC becomes too complex, extract it.
- Vue-related Logic (uses refs, computed, lifecycle hooks, etc.): Extract to a composable function (e.g.,
@/composables/useMyLogic.ts).// Example: @/composables/useCounter.js import {ref} from 'vue'; export function useCounter() { const count = ref(0); function increment() { count.value++; } return {count, increment}; }
- Generic JavaScript/TypeScript Logic (no Vue dependencies): Extract to a utility file (e.g.,
@/utils/helpers.ts).// Example: @/utils/formatDate.js export function formatDate(date) { return new Date(date).toLocaleDateString(); // Example implementation }
- NPM Packages:
- Minimize Dependencies: Avoid adding small NPM packages for simple tasks. Writing a few lines of custom utility code is preferred over introducing dependencies that might complicate updates or cause conflicts later. Aim for a lean
package.json. - Acceptable Dependencies:
- Large, essential functionalities (e.g., internationalization -
vue-i18nis a likely candidate). - Well-established, high-quality packages from the Vue ecosystem.
- Potential Exception: VueUse offers many useful composables and is widely adopted; using it can be acceptable if it significantly simplifies implementation.
- Large, essential functionalities (e.g., internationalization -
- Keep Dependencies Updated: Regularly update all project dependencies.
- Frequency: Aim to check for and apply updates frequently (e.g., weekly). This usually takes minimal time.
- Benefits: Ensures the initial release is modern and secure. Reduces long-term maintenance burden; it's far easier to manage incremental updates (minor and major versions) over time than to face massive, potentially breaking changes after years of neglect. Continue this practice throughout the project's lifecycle.
- Minimize Dependencies: Avoid adding small NPM packages for simple tasks. Writing a few lines of custom utility code is preferred over introducing dependencies that might complicate updates or cause conflicts later. Aim for a lean
- Selectors:
- Primarily use Classes: Apply classes to elements for styling.
- Avoid IDs: IDs can cause conflicts, especially in reusable components. If an ID is technically needed (e.g., for
aria-labelledbyor formlabel'sforattribute), generate it dynamically to ensure uniqueness (e.g.,id="name-of-component-${uniqueId}"). - Rarely use Tag Selectors: Acceptable for base elements like
html,body,main. Avoid for potentially reused tags likeheader,footer- use classes instead.
!important: Do not use it. Find alternative solutions using specificity or restructuring CSS/HTML.- CSS Reset/Normalization: Handled by Beer CSS. The project adds an explicit
box-sizing: border-boxrule for*,*::before,*::afterfor robustness, as Beer CSS might only apply it to*.*, *::before, *::after { box-sizing: border-box; }
@import: Acceptable within Vue SFCs or main CSS files (like@/assets/main.css). Vite/Vue compiles the CSS, mitigating the performance issues@importcauses in traditional CSS (sequential loading, blocking parallel downloads). Useful for organizing large CSS blocks (e.g., font declarations).- Future Consideration: Explore Lightning CSS via Vite integration (Example Article) for CSS optimization and transpilation.
- Logical Properties (LTR/RTL Support):
- Always use logical properties:
margin-inline-startinstead ofmargin-left,padding-block-endinstead ofpadding-bottom,border-inline-startinstead ofborder-left, etc. - This ensures layout adapts correctly if Right-to-Left (RTL) language support is added later.
- Beer CSS already uses logical properties for its components. Apply this rule to all custom CSS.
- Always use logical properties:
- Accessibility (Units):
- Consider accessibility when choosing CSS units for custom styles. Read Josh Comeau's article on Pixels and Accessibility.
- Beer CSS primarily uses
rem, which might affect achieving perfect text resizing according to WCAG guidelines (like the 200% rule). While sufficient for now, be mindful of this limitation.
- Modern CSS Features:
- Feel free to use modern CSS features supported by the latest versions of Evergreen Browsers (Chrome, Firefox, Safari, Edge - roughly features from 2022 onwards).
:has()is considered required. - Compatibility with older browsers (IE, legacy Edge, old Opera) is not a priority.
- Future Idea: Use
@supportsqueries to detect unsupported features in a user's browser and potentially display a message suggesting an update.
- Feel free to use modern CSS features supported by the latest versions of Evergreen Browsers (Chrome, Firefox, Safari, Edge - roughly features from 2022 onwards).
- CSS Layers (
@layer): Acknowledged as a powerful feature for managing specificity, but currently not used in this project to avoid adding complexity for developers unfamiliar with them. - Container Queries:
- Beer CSS provides basic responsive classes (
.s,.m,.l), which might be insufficient for complex components. - Use Container Queries for fine-grained, element-based responsiveness instead of relying solely on viewport-based Media Queries.
- Learning Resources:
- Beer CSS provides basic responsive classes (
Recent user feedback highlighted a desire to edit data directly within the detail dialog, rather than navigating to a separate edit page. While the lead developer dislikes editing in dialogs due to potential data loss, user needs are paramount.
Proposed Solutions for Inline Editing:
- Full-Page Modal: The fields are disabled until Edit mode is entered:
- When the "Edit" button is clicked, the dialog transforms into a full-page view that cannot be accidentally closed (e.g., by clicking outside).
- Closing requires explicit action ("Save Changes" or "Cancel" buttons at the bottom of the Dialog).
- A confirmation prompt should appear if changes were made before cancelling/closing.
- Locked Dialog: The dialog remains a dialog but becomes locked (impossible to close without confirmation) in edit mode:
- Edit mode is activated from interacting with a field.
- Cannot be closed by clicking the overlay/backdrop.
- The standard close button (e.g., 'X') is hidden or disabled.
- Users must explicitly click "Save Changes" or "Cancel Edit" buttons to exit the edit mode.
- Enhanced Current Dialog + Redirect: Keep the current dialog structure but:
- Enable form fields within the dialog when "Edit" is toggled (they are currently disabled in view mode).
- When a user interacts with an enabled field (e.g., clicks/focuses), immediately redirect them to the main Edit Page.
- Crucially: Scroll the Edit Page to the corresponding field and provide a visual cue (e.g., brief highlight/flash) so the user knows exactly where to continue editing. This addresses the user complaints about "breaking the flow" and "having to find the data again."
A decision needs to be made on which approach best balances user convenience and data integrity. Leaning towards Full-Page Modal.