diff --git a/.github/specs/active/profile-button-menu/feat-profile-icon-button-menu.md b/.github/specs/active/profile-button-menu/feat-profile-icon-button-menu.md new file mode 100644 index 0000000..9304f11 --- /dev/null +++ b/.github/specs/active/profile-button-menu/feat-profile-icon-button-menu.md @@ -0,0 +1,63 @@ +# Feature: Replace the text-based "Parent" button with an image icon and modernize the dropdown menu + +## Visual Reference: + +- **Sample Design:** #mockup.png +- **Design:** + 1. Dropdown header colors need to match color theme inside #colors.css + 2. The three dropdown items should be "Profile", "Child Mode", and "Sign out" + 3. Currently, the dropdown shows "Log out" for "Child Mode", that should be changed to "Child Mode" + +## Context: + +- **Goal:** I want a user image icon to display in place of the current "Parent" button +- **User Story:** As a [user], I want to see the image assigned in my profile as an icon button at the top right of the screen. When I click the button I want to see a dropdown appear if I'm in 'parent mode.' I to have the options to see/edit my profile, go back to child mode, or sign out. In child mode, I want the button to trigger the parent pin modal if clicked. + +## Technical Requirements + +- **File Affected:** LoginButton.vue, ParentLayout.vue, ChildLayout.vue, AuthLayout.vue +- **Navigation:** + 1. When the avatar button is focused, pressing Enter or Space opens the dropdown. + 2. When the dropdown is open: + - Up/Down arrow keys move focus between menu items. + - Enter or Space activates the focused menu item. + - Esc closes the dropdown and returns focus to the avatar button. + 3. Tabbing away from the dropdown closes it. +- **ARIA:** + 1. The avatar button must have aria-haspopup="menu" and aria-expanded reflecting the dropdown state. + 2. The dropdown menu must use role="menu", and each item must use role="menuitem". + 3. The currently focused menu item should have aria-selected="true". +- **Focus Ring:** All interactive elements (avatar button and dropdown menu items) must display a visible focus ring when focused via keyboard navigation. The focus ring color should use a theme variable from colors.css and meet accessibility contrast guidelines. +- **Mobile & Layout:** + 1. The avatar icon button must always be positioned at the top right of the screen, regardless of device size. + 2. The icon must never exceed 65px in width or height. + 3. On mobile, ensure the button is at least 44x44px for touch accessibility. +- **Avatar Fallback:** If user.first_name does not exist, display a ? as the fallback initial. +- **Dropdown Placement and Animation:** + 1. The dropdown menu must always appear directly below the avatar icon, right-aligned to the screen edge. + 2. Use a slide down/up animation for showing/hiding the dropdown. +- **State Requirements:** + - Collapsed: Button shows the user.image_id or a fallback icon with the initial of the user.first_name + - Expanded: Shows the dropdown with the three menu options shown in the #mockup.png. -**Menu Item Icons:**: For now, use a stub element or placeholder for each menu item icon, to be replaced with real icons later. +- **Logic:** + 1. Clicking an item in the dropdown should already be implemented. Do not change this. + 2. When clicking a menu item or clicking outside the menu, collapse the menu. + 3. When in 'child mode' (parent not authenticated), show the parent PIN modal or create PIN view (/parent/pin-setup) if user.pin doesn't exist or is empty. (this is already implemented) + +## UI Acceptance Criteria (The "Definition of Done") + +- [ ] UI: Swap the "Parent" button with the user's avatar image. +- [ ] UI: Refactor #LoginButton.vue to use new CSS generated from #mockup.png +- [ ] Logic: Make sure the dropdown does not show when in child mode. +- [ ] Logic: Make sure the parent PIN modal shows when the button is pressed in child mode. +- [ ] Logic: Make sure the parent PIN creation view shows when the button is pressed in child mode if no user.pin doesn't exist or is empty. +- [ ] Frontend Tests: Add vitest for this feature in the frontend to make sure the logic for button clicking in parent mode and child mode act correctly. + 1. [ ] Avatar button renders image, initial, or ? as fallback + 2. [ ] Dropdown opens/closes via click, Enter, Space, Esc, and outside click. + 3. [ ] Dropdown is positioned and animated correctly. + 4. [ ] Keyboard navigation (Up/Down, Enter, Space, Esc) works as specified. + 5. [ ] ARIA attributes and roles are set correctly. + 6. [ ] Focus ring is visible and uses theme color. + 7. [ ] Avatar button meets size and position requirements on all devices. + 8. [ ] Menu logic for parent/child mode is correct. + 9. [ ] Stub icons are rendered for menu items. diff --git a/.github/specs/active/profile-button-menu/mockup.png b/.github/specs/active/profile-button-menu/mockup.png new file mode 100644 index 0000000..56a4300 Binary files /dev/null and b/.github/specs/active/profile-button-menu/mockup.png differ diff --git a/.github/specs/archive/no-delete-system-tasks-and-rewards.md b/.github/specs/archive/feat-no-delete-system-tasks-and-rewards.md similarity index 100% rename from .github/specs/archive/no-delete-system-tasks-and-rewards.md rename to .github/specs/archive/feat-no-delete-system-tasks-and-rewards.md diff --git a/frontend/vue-app/src/layout/AuthLayout.vue b/frontend/vue-app/src/layout/AuthLayout.vue index f65e0e7..e77ec79 100644 --- a/frontend/vue-app/src/layout/AuthLayout.vue +++ b/frontend/vue-app/src/layout/AuthLayout.vue @@ -119,5 +119,6 @@ const showBack = computed( height: 0; overflow: hidden; overflow-y: visible; + padding: 1rem; } diff --git a/frontend/vue-app/src/layout/ChildLayout.vue b/frontend/vue-app/src/layout/ChildLayout.vue index 0c8da80..e596a77 100644 --- a/frontend/vue-app/src/layout/ChildLayout.vue +++ b/frontend/vue-app/src/layout/ChildLayout.vue @@ -118,5 +118,6 @@ const showBack = computed(() => route.path !== '/child') height: 0; overflow: hidden; overflow-y: visible; + padding: 1rem; } diff --git a/frontend/vue-app/src/layout/ParentLayout.vue b/frontend/vue-app/src/layout/ParentLayout.vue index 6489ce2..c10c935 100644 --- a/frontend/vue-app/src/layout/ParentLayout.vue +++ b/frontend/vue-app/src/layout/ParentLayout.vue @@ -292,6 +292,7 @@ onMounted(async () => { height: 0; overflow: hidden; overflow-y: visible; + padding: 1rem; } .app-version {