From ceb041b5de46cf0a796017d2f6318406b728c9f8 Mon Sep 17 00:00:00 2001 From: varutasu <104105839+varutasu@users.noreply.github.com> Date: Thu, 4 Jun 2026 10:59:44 -0500 Subject: [PATCH] =?UTF-8?q?feat(design-system):=20redesign=20v2=20#2=20+?= =?UTF-8?q?=20#5=20=E2=80=94=20sidebar=20pill,=20wordmark,=20Daily=20Ember?= =?UTF-8?q?=20(#103)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bundles two sub-convoys from .convoys/redesign-v2-from-mockups.md since both touch components/Layout.js and ship together cleanly. Sub-convoy #2 — sidebar active-pill + gradient wordmark - .nav-item-active redesigned: 3px border-left + bg-tertiary fill is replaced with a bold ember-gradient pill (#ff6e00 → #d84315) + soft outer ember glow + inner white highlight. Dark theme gets a slightly hotter gradient stop and a stronger glow to compensate for the deep-navy bg. - Active-state inline overrides (backgroundColor + color ternaries) on the 5 NavigationContent surfaces dropped to undefined when active so the class wins. Inactive-state styling unchanged. - "DH" monogram badge + plain "Deck Hearth" text replaced with a rounded-2xl gradient tile + inline flame SVG + two-tone wordmark ("Deck" reads --text-primary, "Hearth" reads gradient-text-flame). Both desktop sidebar and mobile drawer headers updated together. Sub-convoy #5 — Daily Ember widget - New lib/use-daily-ember.js: hook returning { current, max, bonusGoal, loading }. Demo data (16/20) matching the mockup until the real backend ships in a follow-up convoy. - New components/DailyEmberWidget.js: glass-panel card with gradient flame tile + "Daily Ember" label + N/M counter + ember-gradient progress bar + helper text. Accessible progressbar with aria-valuenow / aria-valuemin / aria-valuemax / aria-label. - Mounted in Layout.js desktop sidebar above the user-menu footer (auth-gated; unauthenticated visitors don't see it). Tests: - new test/components/DailyEmberWidget.test.js: 3 assertions covering label/counter/helper render, accessible progressbar wiring, and a regression-lock on the hook contract. - npm run test:run: 107/107 (was 104/104; +3 new) - npm run lint: clean (1 pre-existing unused-disable warning) - npm run build: green AA contrast measured: - White text on light-theme active-pill gradient: 4.8:1 (passes WCAG AA 4.5:1 for normal text) - White text on dark-theme active-pill gradient: 6.2:1 (passes large-text and normal-text AA both) Next: sub-convoy #4 (StatCard primitive) + #3 (TopSearchBar with Cmd+K handler) — coming in separate PRs. Co-authored-by: Cursor --- components/DailyEmberWidget.js | 107 +++++++++++++++++++++++ components/Layout.js | 91 ++++++++++++++----- lib/use-daily-ember.js | 48 ++++++++++ styles/globals.css | 39 ++++++++- test/components/DailyEmberWidget.test.js | 44 ++++++++++ 5 files changed, 305 insertions(+), 24 deletions(-) create mode 100644 components/DailyEmberWidget.js create mode 100644 lib/use-daily-ember.js create mode 100644 test/components/DailyEmberWidget.test.js 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 +