diff --git a/components/DailyEmberWidget.js b/components/DailyEmberWidget.js new file mode 100644 index 0000000..ffee6ca --- /dev/null +++ b/components/DailyEmberWidget.js @@ -0,0 +1,107 @@ +import { useDailyEmber } from '../lib/use-daily-ember'; + +/** + * DailyEmberWidget — sidebar gamification card. + * + * Renders the user's current Daily Ember progress as a small glass- + * panel card with a flame icon, "Daily Ember" label, "N / M" current/ + * max display, ember-gradient horizontal progress bar, and a helper + * line. From the operator's redesign-v2 mockup (2026-06-04). Mounted + * in Layout.js's desktop sidebar above the user-menu footer. + * + * The widget consumes `useDailyEmber()` for state; that hook returns + * demo data (16 / 20) until the real backend ships in a separate + * convoy. See lib/use-daily-ember.js for the wiring TODO. + * + * Props: none. The component is self-contained on purpose so it can + * be dropped into any sidebar variant (mobile drawer, settings page, + * future onboarding wizard) without prop drilling. + * + * Accessibility: + * - The progress bar uses role="progressbar" with aria-valuenow, + * aria-valuemin, aria-valuemax, and aria-label so a screen reader + * announces the current ember count. + * - The flame icon has aria-hidden="true" (decorative; the label + * "Daily Ember" carries the meaning). + */ +export default function DailyEmberWidget() { + const { current, max, bonusGoal, loading } = useDailyEmber(); + + if (loading) { + return null; // Sidebar prefers no widget over a half-loaded one. + } + + const percent = Math.min(100, Math.max(0, (current / max) * 100)); + const ariaLabel = `Daily Ember progress: ${current} of ${max}`; + const helperLine = + current >= bonusGoal + ? 'Bonus XP unlocked! Come back tomorrow.' + : `Collect ${bonusGoal} embers for bonus XP!`; + + return ( +
+
+ +
+
+ Daily Ember +
+
+ {current} / {max} +
+
+
+
+
+
+

+ {helperLine} +

+
+ ); +} diff --git a/components/Layout.js b/components/Layout.js index 7c6c017..e53a219 100644 --- a/components/Layout.js +++ b/components/Layout.js @@ -3,6 +3,7 @@ import { useRouter } from 'next/router'; import Link from 'next/link'; import { useTheme } from '../lib/theme-context'; import MobileNavigation from './MobileNavigation'; +import DailyEmberWidget from './DailyEmberWidget'; // User Profile Dropdown Component function UserProfileDropdown({ user, onMobileMenuClose }) { @@ -324,8 +325,8 @@ function NavigationContent({ user, router, onItemClick }) { } `} style={{ - backgroundColor: item.active ? 'var(--bg-tertiary)' : 'transparent', - color: item.active ? 'var(--text-primary)' : 'var(--text-secondary)', + backgroundColor: item.active ? undefined : 'transparent', + color: item.active ? undefined : 'var(--text-secondary)', '--tw-ring-color': 'var(--accent-ember)', '--tw-ring-offset-color': 'var(--bg-secondary)' }} @@ -357,8 +358,8 @@ function NavigationContent({ user, router, onItemClick }) { } `} style={{ - backgroundColor: router.pathname === '/dashboard' ? 'var(--bg-tertiary)' : 'transparent', - color: router.pathname === '/dashboard' ? 'var(--text-primary)' : 'var(--text-secondary)', + backgroundColor: router.pathname === '/dashboard' ? undefined : 'transparent', + color: router.pathname === '/dashboard' ? undefined : 'var(--text-secondary)', '--tw-ring-color': 'var(--accent-ember)', '--tw-ring-offset-color': 'var(--bg-secondary)' }} @@ -402,8 +403,8 @@ function NavigationContent({ user, router, onItemClick }) { } `} style={{ - backgroundColor: subItem.active ? 'var(--bg-tertiary)' : 'transparent', - color: subItem.active ? 'var(--text-primary)' : 'var(--text-secondary)', + backgroundColor: subItem.active ? undefined : 'transparent', + color: subItem.active ? undefined : 'var(--text-secondary)', '--tw-ring-color': 'var(--accent-ember)', '--tw-ring-offset-color': 'var(--bg-secondary)' }} @@ -455,8 +456,8 @@ function NavigationContent({ user, router, onItemClick }) { } `} style={{ - backgroundColor: item.active ? 'var(--bg-tertiary)' : 'transparent', - color: item.active ? 'var(--text-primary)' : 'var(--text-secondary)', + backgroundColor: item.active ? undefined : 'transparent', + color: item.active ? undefined : 'var(--text-secondary)', '--tw-ring-color': 'var(--accent-ember)', '--tw-ring-offset-color': 'var(--bg-secondary)' }} @@ -533,8 +534,8 @@ function NavigationContent({ user, router, onItemClick }) { } `} style={{ - backgroundColor: subItem.active ? 'var(--bg-tertiary)' : 'transparent', - color: subItem.active ? 'var(--text-primary)' : 'var(--text-secondary)', + backgroundColor: subItem.active ? undefined : 'transparent', + color: subItem.active ? undefined : 'var(--text-secondary)', '--tw-ring-color': 'var(--accent-ember)', '--tw-ring-offset-color': 'var(--bg-secondary)' }} @@ -571,8 +572,8 @@ function NavigationContent({ user, router, onItemClick }) { } `} style={{ - backgroundColor: item.active ? 'var(--bg-tertiary)' : 'transparent', - color: item.active ? 'var(--text-primary)' : 'var(--text-secondary)', + backgroundColor: item.active ? undefined : 'transparent', + color: item.active ? undefined : 'var(--text-secondary)', '--tw-ring-color': 'var(--accent-ember)', '--tw-ring-offset-color': 'var(--bg-secondary)' }} @@ -643,10 +644,29 @@ export default function Layout({ children, user = null, showSearch = false }) { {/* Mobile Header with Close Button */}
-
- DH + -

Deck Hearth

+

+ Deck + Hearth +