diff --git a/app/src/components/IngredientReadView.tsx b/app/src/components/IngredientReadView.tsx index 740e39c40..b5eb86700 100644 --- a/app/src/components/IngredientReadView.tsx +++ b/app/src/components/IngredientReadView.tsx @@ -21,6 +21,7 @@ interface IngredientReadViewProps { title: string; subtitle?: string; buttonLabel?: string; + emptyState?: React.ReactNode; onBuild?: () => void; isLoading: boolean; isError: boolean; @@ -37,6 +38,7 @@ export default function IngredientReadView({ title, subtitle, buttonLabel, + emptyState, onBuild, isLoading, isError, @@ -81,21 +83,6 @@ export default function IngredientReadView({ - {/* Title Section */} -
- - {title} - -
- {/* Content Section */}
{data.length === 0 ? (
- + {emptyState || }
) : ( diff --git a/app/src/components/StandardLayout.tsx b/app/src/components/StandardLayout.tsx index c11bfbf9f..a855472a2 100644 --- a/app/src/components/StandardLayout.tsx +++ b/app/src/components/StandardLayout.tsx @@ -56,7 +56,7 @@ export default function StandardLayout({ children }: StandardLayoutProps) { > -
+
{children}
diff --git a/app/src/components/calculator/CalculatorLaunchPage.tsx b/app/src/components/calculator/CalculatorLaunchPage.tsx new file mode 100644 index 000000000..be11d3ae6 --- /dev/null +++ b/app/src/components/calculator/CalculatorLaunchPage.tsx @@ -0,0 +1,372 @@ +import { IconArrowRight, IconHome, IconScale, IconShare3 } from '@tabler/icons-react'; +import { Button, Container, Group, Stack, Text, Title } from '@/components/ui'; +import { WEBSITE_URL } from '@/constants'; +import { useAppNavigate } from '@/contexts/NavigationContext'; +import { colors, spacing, typography } from '@/designTokens'; +import { useCurrentCountry } from '@/hooks/useCurrentCountry'; +import type { CountryId } from '@/libs/countries'; + +type SupportedCountry = 'us' | 'uk'; +type LaunchContent = { + eyebrow: string; + title: string; + subtitle: string; + householdCta: string; + reportCta: string; + householdDescription: string; + reportDescription: string; + outputs: string[]; + explainer: string; +}; + +const DEFAULT_LAUNCH_CONTENT: LaunchContent = { + eyebrow: 'Household and policy analysis', + title: 'Run household calculations and policy reports', + subtitle: + 'Model a custom household, compare current law to reforms, and inspect income, taxes, benefits, and poverty impacts in one place.', + householdCta: 'Start household calculation', + reportCta: 'Build population report', + householdDescription: + 'Check a household against current policy or a reform without starting from the full report workflow.', + reportDescription: + 'Generate national or regional analyses for distribution, budget, and poverty impacts.', + outputs: [ + 'Household disposable income', + 'Tax and benefit changes', + 'Shareable and reproducible results', + ], + explainer: + 'Saved work stays in this browser unless you create a share link, so first-run users can start immediately without an account.', +}; + +const LAUNCH_CONTENT: Record = { + us: { + eyebrow: 'Poverty and household policy analysis', + title: 'Run Supplemental Poverty Measure calculations and policy reports', + subtitle: + 'Model a custom household, compare current law to reforms, and inspect SPM poverty, net income, taxes, and benefits in one place.', + householdCta: 'Start household calculation', + reportCta: 'Build population report', + householdDescription: + 'Check a family or individual against current law and proposed reforms with a faster path into the household builder.', + reportDescription: + 'Generate nationwide, state, or district analyses for budget, distribution, and poverty impacts.', + outputs: [ + 'SPM poverty status and gap', + 'Net income, taxes, and benefits', + 'Shareable and reproducible results', + ], + explainer: + 'Saved work stays in this browser unless you create a share link, so first-run users can start immediately without an account.', + }, + uk: { + eyebrow: 'Household and policy analysis', + title: 'Run household calculations and poverty impact reports', + subtitle: + 'Model a custom household, compare current law to reforms, and inspect income, taxes, benefits, and poverty impacts in one place.', + householdCta: 'Start household calculation', + reportCta: 'Build population report', + householdDescription: + 'Check a household against current policy or a reform without starting from the full report workflow.', + reportDescription: + 'Generate nationwide or regional analyses for distribution, budget, and poverty impacts.', + outputs: [ + 'Household disposable income', + 'Tax and benefit changes', + 'Shareable and reproducible results', + ], + explainer: + 'Saved work stays in this browser unless you create a share link, so first-run users can start immediately without an account.', + }, +}; + +export function getLaunchContent(countryId: CountryId): LaunchContent { + return LAUNCH_CONTENT[countryId as SupportedCountry] ?? DEFAULT_LAUNCH_CONTENT; +} + +function FeatureCard({ + icon, + title, + description, +}: { + icon: React.ReactNode; + title: string; + description: string; +}) { + return ( +
+
+ {icon} +
+ + {title} + + {description} +
+ ); +} + +export default function CalculatorLaunchPage() { + const countryId = useCurrentCountry(); + const nav = useAppNavigate(); + const content = getLaunchContent(countryId); + + return ( + + +
+
+
+ +
+
+ + {content.eyebrow} + + + {content.title} + + + {content.subtitle} + + + + + + + + {content.explainer} + +
+ +
+
+ + What you can inspect + +
    + {content.outputs.map((output) => ( +
  • +
    + {output} +
  • + ))} +
+
+ +
+ + Good launch posture + + + Clear household entry, shareable report links, and a separate saved-work area so + first-time visitors do not land on an empty internal screen. + +
+
+
+
+ +
+ } + title="Household calculations" + description={content.householdDescription} + /> + } + title="Population-wide reports" + description={content.reportDescription} + /> + } + title="Sharing and reproducibility" + description="Copy a share link for report output, keep local saved work for iteration, and jump to the Python reproduction view when you need exact implementation details." + /> +
+ +
+
+ + Recommended first steps + +
    +
  1. + + Start with a household calculation if you want an immediate answer for one family + or individual. + +
  2. +
  3. + + Build a population report if you want national, state, or district-level poverty + and budget effects. + +
  4. +
  5. + + Use saved work for iteration and share links only once the output is ready to + send. + +
  6. +
+
+ + +
+
+
+ ); +} diff --git a/app/src/pages/ReportBuilder.page.tsx b/app/src/pages/ReportBuilder.page.tsx index a411da562..3afef7040 100644 --- a/app/src/pages/ReportBuilder.page.tsx +++ b/app/src/pages/ReportBuilder.page.tsx @@ -163,14 +163,14 @@ const INGREDIENT_COLORS = { icon: colors.primary[600], bg: colors.primary[50], border: colors.primary[200], - accent: colors.primary[500], + accent: colors.primary[600], }, dynamics: { // Muted gray-green for dynamics (distinct from teal and slate) icon: colors.gray[500], bg: colors.gray[50], border: colors.gray[200], - accent: colors.gray[400], + accent: colors.gray[500], }, }; @@ -1200,7 +1200,7 @@ function SimulationBlock({ {isRequired && ( - + Required )} @@ -1601,11 +1601,11 @@ function IngredientPickerModal({ {/* Header row */} ) : ( - + No changes yet )} @@ -4264,7 +4264,7 @@ function PopulationBrowseModal({ ) : ( - + No members yet )} @@ -5518,12 +5518,12 @@ function ReportMetaPanel({ {/* Ready message */} {isReportConfigured && ( - + Ready to run your analysis diff --git a/app/src/pages/Reports.page.tsx b/app/src/pages/Reports.page.tsx index 1c9ed3721..68f2ecaf5 100644 --- a/app/src/pages/Reports.page.tsx +++ b/app/src/pages/Reports.page.tsx @@ -1,5 +1,5 @@ import { useEffect, useMemo, useState } from 'react'; -import { IconSettings } from '@tabler/icons-react'; +import { IconArrowRight, IconSettings } from '@tabler/icons-react'; import { useSelector } from 'react-redux'; import { BulletsValue, @@ -12,9 +12,10 @@ import { RenameIngredientModal } from '@/components/common/RenameIngredientModal import IngredientReadView from '@/components/IngredientReadView'; import { MultiSimOutputTypeCell } from '@/components/report/MultiSimReportOutputTypeCell'; import { ReportOutputTypeCell } from '@/components/report/ReportOutputTypeCell'; -import { Stack } from '@/components/ui'; +import { Button, Group, Stack, Text, Title } from '@/components/ui'; import { MOCK_USER_ID } from '@/constants'; import { useAppNavigate } from '@/contexts/NavigationContext'; +import { colors, spacing, typography } from '@/designTokens'; import { useCurrentCountry } from '@/hooks/useCurrentCountry'; import { useDisclosure } from '@/hooks/useDisclosure'; import { useUpdateReportAssociation } from '@/hooks/useUserReportAssociations'; @@ -51,6 +52,10 @@ export default function ReportsPage() { nav.push(targetPath); }; + const handleBuildHousehold = () => { + nav.push(`/${countryId}/households/create?scope=household`); + }; + const handleCloseRename = () => { closeRename(); setRenamingReportId(null); @@ -205,13 +210,46 @@ export default function ReportsPage() { [data, countryId, currentLawId] ); + const emptyState = ( + + + No saved analyses yet + + + Start with a household calculation if you want an immediate answer for one family, or build + a population-wide report for national, state, or district analysis. + + + + + + + Saved work stays in this browser unless you create a share link from report output. + + + ); + return ( <> void; @@ -32,6 +34,8 @@ interface PopulationPathwayWrapperProps { export default function PopulationPathwayWrapper({ onComplete }: PopulationPathwayWrapperProps) { const countryId = useCurrentCountry(); const nav = useAppNavigate(); + const location = useAppLocation(); + const hasAppliedLaunchPrefill = useRef(false); const handleCancel = useCallback(() => { nav.push(`/${countryId}/households`); @@ -83,6 +87,19 @@ export default function PopulationPathwayWrapper({ onComplete }: PopulationPathw } }, [isValidMode, currentMode, nav, countryId]); + useEffect(() => { + if (hasAppliedLaunchPrefill.current || currentMode !== StandalonePopulationViewMode.SCOPE) { + return; + } + + if (getPopulationLaunchPrefill(location.search) !== 'household') { + return; + } + + hasAppliedLaunchPrefill.current = true; + populationCallbacks.handleScopeSelected(null, 'household'); + }, [currentMode, location.search, populationCallbacks]); + // ========== VIEW RENDERING ========== let currentView: React.ReactElement; diff --git a/app/src/pathways/population/launchPrefill.ts b/app/src/pathways/population/launchPrefill.ts new file mode 100644 index 000000000..5bb718c69 --- /dev/null +++ b/app/src/pathways/population/launchPrefill.ts @@ -0,0 +1,17 @@ +export type PopulationLaunchPrefill = 'household' | null; + +/** + * Parse standalone population launch parameters from the current query string. + * We support both `scope=household` and `scope=custom-household` for shareable + * links and future routing cleanup. + */ +export function getPopulationLaunchPrefill(search: string): PopulationLaunchPrefill { + const params = new URLSearchParams(search); + const scope = params.get('scope'); + + if (scope === 'household' || scope === 'custom-household') { + return 'household'; + } + + return null; +} diff --git a/app/src/tests/unit/components/IngredientReadView.test.tsx b/app/src/tests/unit/components/IngredientReadView.test.tsx index b0b260a28..43794359a 100644 --- a/app/src/tests/unit/components/IngredientReadView.test.tsx +++ b/app/src/tests/unit/components/IngredientReadView.test.tsx @@ -46,6 +46,40 @@ describe('IngredientReadView', () => { expect(screen.getByText(/no policy found/i)).toBeInTheDocument(); }); + test('given custom empty state then renders it instead of the default message', () => { + render( + Start by creating your first analysis.} + isLoading={false} + isError={false} + data={EMPTY_DATA} + columns={MOCK_COLUMNS} + /> + ); + + expect(screen.getByText(/start by creating your first analysis/i)).toBeInTheDocument(); + expect(screen.queryByText(/no policy found/i)).not.toBeInTheDocument(); + }); + + test('given title then renders it only once', () => { + render( + + ); + + expect(screen.getAllByText(MOCK_INGREDIENT.TITLE)).toHaveLength(1); + }); + test('given loading state then displays loader', () => { // When render( diff --git a/app/src/tests/unit/components/calculator/CalculatorLaunchPage.test.tsx b/app/src/tests/unit/components/calculator/CalculatorLaunchPage.test.tsx new file mode 100644 index 000000000..3ae70bef3 --- /dev/null +++ b/app/src/tests/unit/components/calculator/CalculatorLaunchPage.test.tsx @@ -0,0 +1,26 @@ +import { renderWithCountry, screen } from '@test-utils'; +import { describe, expect, test } from 'vitest'; +import CalculatorLaunchPage from '@/components/calculator/CalculatorLaunchPage'; + +describe('CalculatorLaunchPage', () => { + test('given US country then renders SPM-specific launch copy', () => { + renderWithCountry(, 'us', '/us', 'calculator'); + + expect( + screen.getByRole('heading', { + name: /run supplemental poverty measure calculations and policy reports/i, + }) + ).toBeInTheDocument(); + }); + + test('given non-US or UK country then renders generic launch copy', () => { + renderWithCountry(, 'ca', '/ca', 'calculator'); + + expect( + screen.getByRole('heading', { + name: /run household calculations and policy reports/i, + }) + ).toBeInTheDocument(); + expect(screen.queryByText(/supplemental poverty measure/i)).not.toBeInTheDocument(); + }); +}); diff --git a/app/src/tests/unit/pages/Populations.page.test.tsx b/app/src/tests/unit/pages/Populations.page.test.tsx index 94f674488..32801c2a6 100644 --- a/app/src/tests/unit/pages/Populations.page.test.tsx +++ b/app/src/tests/unit/pages/Populations.page.test.tsx @@ -92,7 +92,7 @@ describe('PopulationsPage', () => { // Then expect( - screen.getByRole('heading', { name: 'Your saved households', level: 2 }) + screen.getByRole('heading', { name: 'Your saved households', level: 1 }) ).toBeInTheDocument(); expect(screen.getByText(POPULATION_LABELS.PAGE_SUBTITLE)).toBeInTheDocument(); }); @@ -445,7 +445,7 @@ describe('PopulationsPage', () => { // Then // The component should render successfully without connections column expect( - screen.getByRole('heading', { name: 'Your saved households', level: 2 }) + screen.getByRole('heading', { name: 'Your saved households', level: 1 }) ).toBeInTheDocument(); // Verify data is displayed correctly expect(screen.getByText(POPULATION_LABELS.HOUSEHOLD_1)).toBeInTheDocument(); diff --git a/app/src/tests/unit/pages/Reports.page.test.tsx b/app/src/tests/unit/pages/Reports.page.test.tsx index 7e5c55fb3..80cd7ab90 100644 --- a/app/src/tests/unit/pages/Reports.page.test.tsx +++ b/app/src/tests/unit/pages/Reports.page.test.tsx @@ -119,8 +119,10 @@ describe('ReportsPage', () => { render(); // Then - expect(screen.getByText('Your saved reports')).toBeInTheDocument(); - expect(screen.getByText(/Generate comprehensive impact analyses/)).toBeInTheDocument(); + expect(screen.getByText('Saved analyses')).toBeInTheDocument(); + expect( + screen.getByText(/Build household calculations and population-wide reports/i) + ).toBeInTheDocument(); expect(screen.getByText('Data count: 2')).toBeInTheDocument(); }); diff --git a/app/src/tests/unit/pathways/population/launchPrefill.test.ts b/app/src/tests/unit/pathways/population/launchPrefill.test.ts new file mode 100644 index 000000000..9ee96d0c4 --- /dev/null +++ b/app/src/tests/unit/pathways/population/launchPrefill.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, test } from 'vitest'; +import { getPopulationLaunchPrefill } from '@/pathways/population/launchPrefill'; + +describe('getPopulationLaunchPrefill', () => { + test('given household scope then returns household prefill', () => { + expect(getPopulationLaunchPrefill('?scope=household')).toBe('household'); + }); + + test('given custom-household scope then returns household prefill', () => { + expect(getPopulationLaunchPrefill('?scope=custom-household')).toBe('household'); + }); + + test('given unrelated query then returns null', () => { + expect(getPopulationLaunchPrefill('?scope=national')).toBeNull(); + expect(getPopulationLaunchPrefill('?view=reports')).toBeNull(); + expect(getPopulationLaunchPrefill('')).toBeNull(); + }); +}); diff --git a/calculator-app/src/app/[countryId]/page.tsx b/calculator-app/src/app/[countryId]/page.tsx index 2c7237f6c..5f7f90dcf 100644 --- a/calculator-app/src/app/[countryId]/page.tsx +++ b/calculator-app/src/app/[countryId]/page.tsx @@ -1,23 +1,15 @@ "use client"; -import { use, useEffect } from "react"; -import { useRouter } from "next/navigation"; - -/** - * Country index route — redirects to /:countryId/reports. - * Mirrors the React Router . - */ -export default function CountryIndexRoute({ - params, -}: { - params: Promise<{ countryId: string }>; -}) { - const { countryId } = use(params); - const router = useRouter(); - - useEffect(() => { - router.replace(`/${countryId}/reports`); - }, [router, countryId]); - - return null; +import StandardLayout from "@/components/StandardLayout"; +import CalculatorLaunchPage from "@/components/calculator/CalculatorLaunchPage"; +import { CalculatorProviders } from "./providers"; + +export default function CountryIndexRoute() { + return ( + + + + + + ); } diff --git a/calculator-app/src/app/layout.tsx b/calculator-app/src/app/layout.tsx index 1356ba159..25fc37d1e 100644 --- a/calculator-app/src/app/layout.tsx +++ b/calculator-app/src/app/layout.tsx @@ -2,9 +2,9 @@ import type { Metadata } from "next"; import "./globals.css"; export const metadata: Metadata = { - title: "PolicyEngine Calculator", + title: "PolicyEngine Poverty Calculator", description: - "Calculate your taxes and benefits, create policy simulations, and analyze reform impacts.", + "Run household poverty calculations, build policy reports, and analyze taxes, benefits, and reform impacts.", }; export default function RootLayout({ diff --git a/calculator-app/src/app/page.tsx b/calculator-app/src/app/page.tsx index a63302fcd..0b0135aae 100644 --- a/calculator-app/src/app/page.tsx +++ b/calculator-app/src/app/page.tsx @@ -7,7 +7,7 @@ import { countryIds } from "@/libs/countries"; import { geolocationService } from "@/routing/geolocation/GeolocationService"; /** - * Root page — redirects to /:countryId/reports based on IP geolocation. + * Root page — redirects to /:countryId based on IP geolocation. * Mirrors app/src/routing/RedirectToCountry.tsx for the Next.js app. */ export default function RootPage() { @@ -17,17 +17,20 @@ export default function RootPage() { async function detect() { const cached = getCachedCountry(); if (cached) { - router.replace(`/${cached}/reports`); + router.replace(`/${cached}`); return; } try { const country = await geolocationService.detectCountry(); cacheCountry(country); - router.replace(`/${country}/reports`); + router.replace(`/${country}`); } catch (error) { - console.warn("[RootPage] Geolocation failed, falling back to US:", error); - router.replace("/us/reports"); + console.warn( + "[RootPage] Geolocation failed, falling back to US:", + error, + ); + router.replace("/us"); } } @@ -37,7 +40,7 @@ export default function RootPage() { return (
-
Loading PolicyEngine...
+
Opening PolicyEngine...
); } @@ -54,7 +57,10 @@ function getCachedCountry(): string | null { if (countryIds.includes(country)) { return country; } - console.warn("[RootPage] Cached country is not a valid country ID:", country); + console.warn( + "[RootPage] Cached country is not a valid country ID:", + country, + ); } localStorage.removeItem("detectedCountry"); } catch (error) { diff --git a/website/src/__tests__/components/header.test.tsx b/website/src/__tests__/components/header.test.tsx new file mode 100644 index 000000000..003a648a8 --- /dev/null +++ b/website/src/__tests__/components/header.test.tsx @@ -0,0 +1,32 @@ +import { fireEvent, render, screen } from "@testing-library/react"; +import { beforeEach, describe, expect, test, vi } from "vitest"; + +const pushMock = vi.fn(); +const replaceMock = vi.fn(); + +vi.mock("next/navigation", () => ({ + usePathname: vi.fn(() => "/us"), + useRouter: vi.fn(() => ({ push: pushMock, replace: replaceMock })), +})); + +import Header from "@/components/Header"; + +describe("Header", () => { + beforeEach(() => { + pushMock.mockReset(); + replaceMock.mockReset(); + }); + + test("keeps the mobile menu out of the DOM until it is opened", () => { + render(
); + + const openButton = screen.getByRole("button", { name: "Open menu" }); + expect(openButton).toHaveAttribute("aria-expanded", "false"); + expect(screen.queryByText("Menu")).not.toBeInTheDocument(); + + fireEvent.click(openButton); + + expect(screen.getByText("Menu")).toBeInTheDocument(); + expect(screen.getByRole("button", { name: "Close menu" })).toBeInTheDocument(); + }); +}); diff --git a/website/src/__tests__/pages/research-client.test.tsx b/website/src/__tests__/pages/research-client.test.tsx new file mode 100644 index 000000000..ef1ca2c43 --- /dev/null +++ b/website/src/__tests__/pages/research-client.test.tsx @@ -0,0 +1,54 @@ +import { fireEvent, render, screen, waitFor } from "@testing-library/react"; +import { beforeEach, describe, expect, test, vi } from "vitest"; + +const replaceMock = vi.fn(); + +vi.mock("next/navigation", () => ({ + usePathname: vi.fn(() => "/us/research"), + useRouter: vi.fn(() => ({ replace: replaceMock })), + useSearchParams: vi.fn(() => new URLSearchParams()), +})); + +vi.mock("@/components/blog/useDisplayCategory", () => ({ + useDisplayCategory: vi.fn(() => "desktop"), +})); + +vi.mock("@/hooks/useInfiniteScroll", () => ({ + useInfiniteScroll: vi.fn(() => ({ + visibleCount: 8, + sentinelRef: { current: null }, + hasMore: false, + reset: vi.fn(), + })), +})); + +import ResearchClient from "@/app/[countryId]/research/ResearchClient"; + +describe("ResearchClient", () => { + beforeEach(() => { + replaceMock.mockReset(); + }); + + test("exposes labeled filter checkboxes and updates the query string when toggled", async () => { + render(); + + await waitFor(() => { + expect(replaceMock).toHaveBeenCalled(); + }); + replaceMock.mockClear(); + + fireEvent.click(screen.getByRole("button", { name: "Type" })); + + const articleCheckbox = screen.getByRole("checkbox", { name: "Article" }); + expect(articleCheckbox).toBeInTheDocument(); + + fireEvent.click(articleCheckbox); + + await waitFor(() => { + expect(replaceMock).toHaveBeenCalledWith( + expect.stringContaining("types=article"), + { scroll: false }, + ); + }); + }); +}); diff --git a/website/src/app/[countryId]/research/ResearchClient.tsx b/website/src/app/[countryId]/research/ResearchClient.tsx index 8664a1a12..b59e3a89e 100644 --- a/website/src/app/[countryId]/research/ResearchClient.tsx +++ b/website/src/app/[countryId]/research/ResearchClient.tsx @@ -455,7 +455,7 @@ function FilterSection({ minWidth: "20px", height: "20px", borderRadius: "10px", - backgroundColor: colors.primary[500], + backgroundColor: colors.primary[600], color: colors.white, fontSize: "11px", fontWeight: typography.fontWeight.bold, @@ -511,12 +511,11 @@ function CheckboxRow({ onChange: () => void; indented?: boolean; }) { + const labelId = `${id}-label`; + return ( -
{ if (e.key === "Enter" || e.key === " ") { e.preventDefault(); onChange(); } }} +
+ ); } diff --git a/website/src/components/Header.tsx b/website/src/components/Header.tsx index 6462a1534..ad0c4f56c 100644 --- a/website/src/components/Header.tsx +++ b/website/src/components/Header.tsx @@ -440,11 +440,9 @@ function MobileNavLink({ item, onClose }: { item: NavItemSetup; onClose: () => v } function MobileMenu({ - open, onClose, navItems, }: { - open: boolean; onClose: () => void; navItems: NavItemSetup[]; }) { @@ -456,7 +454,6 @@ function MobileMenu({ zIndex: 2000, display: "flex", justifyContent: "flex-end", - pointerEvents: open ? "auto" : "none", }} > {/* Backdrop */} @@ -466,8 +463,6 @@ function MobileMenu({ position: "absolute", inset: 0, backgroundColor: "rgba(0,0,0,0.4)", - opacity: open ? 1 : 0, - transition: "opacity 0.3s ease", }} /> {/* Panel */} @@ -481,8 +476,6 @@ function MobileMenu({ display: "flex", flexDirection: "column", gap: spacing.lg, - transform: open ? "translateX(0)" : "translateX(100%)", - transition: "transform 0.3s cubic-bezier(0.4, 0, 0.2, 1)", }} >
setMobileOpen(true)} - aria-label="Toggle navigation" + aria-label="Open menu" + aria-controls="mobile-navigation" + aria-expanded={mobileOpen} style={{ padding: "4px", borderRadius: "4px", @@ -685,11 +678,11 @@ export default function Header() {
- setMobileOpen(false)} - navItems={navItems} - /> + {mobileOpen && ( +
+ setMobileOpen(false)} navItems={navItems} /> +
+ )}
); } diff --git a/website/src/components/static/ActionButton.tsx b/website/src/components/static/ActionButton.tsx index 4d259c10d..3f568523f 100644 --- a/website/src/components/static/ActionButton.tsx +++ b/website/src/components/static/ActionButton.tsx @@ -40,9 +40,9 @@ export default function ActionButton({ hoverBorder: colors.black, }, secondary: { - backgroundColor: colors.primary[500], + backgroundColor: colors.primary[600], color: colors.white, - border: `2px solid ${colors.primary[500]}`, + border: `2px solid ${colors.primary[600]}`, hoverBackground: colors.primary[600], hoverBorder: colors.primary[600], },