{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "footer-accordion", "title": "Footer — Collapsible Site Map", "description": "A long site map that collapses into accordions below the lg breakpoint and lays out as columns above it.", "registryDependencies": [ "https://ui.digital.nsw.gov.au/registry/r/theme.json", "https://ui.digital.nsw.gov.au/registry/r/footer.json", "https://ui.digital.nsw.gov.au/registry/r/collapsible.json", "https://ui.digital.nsw.gov.au/registry/r/icons.json" ], "files": [ { "path": "src/patterns/footer-accordion.tsx", "content": "'use client'\n\nimport * as React from 'react'\n\nimport { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/collapsible'\nimport { Footer, FooterNavLink } from '@/components/footer'\nimport { IconExpandMore } from '@/icons/expand-more'\n\n// ── Sample content — replace with your service's own ────────────────────────\nconst columnsSample = [\n {\n heading: 'Services',\n links: [\n { name: 'Apply for a licence', href: '#licence' },\n { name: 'Renew a registration', href: '#renew' },\n { name: 'Pay a fine', href: '#fine' },\n { name: 'Book an appointment', href: '#appointment' },\n { name: 'Check an application', href: '#application' },\n ],\n },\n {\n heading: 'About',\n links: [\n { name: 'Who we are', href: '#about' },\n { name: 'Our strategy', href: '#strategy' },\n { name: 'Annual reports', href: '#reports' },\n { name: 'Careers', href: '#careers' },\n { name: 'News', href: '#news' },\n ],\n },\n {\n heading: 'Help',\n links: [\n { name: 'Contact us', href: '#contact' },\n { name: 'Accessibility help', href: '#accessibility-help' },\n { name: 'Give feedback', href: '#feedback' },\n { name: 'Report a problem', href: '#report' },\n { name: 'Interpreting services', href: '#interpreting' },\n ],\n },\n {\n heading: 'Popular',\n links: [\n { name: 'Service NSW', href: '#service-nsw' },\n { name: 'Digital NSW', href: '#digital-nsw' },\n { name: 'Data.NSW', href: '#data-nsw' },\n { name: 'Have your say', href: '#have-your-say' },\n { name: 'NSW Government jobs', href: '#jobs' },\n ],\n },\n]\n\nconst legalLinksSample = [\n { name: 'Accessibility', href: '#accessibility' },\n { name: 'Privacy', href: '#privacy' },\n { name: 'Copyright', href: '#copyright' },\n { name: 'Disclaimer', href: '#disclaimer' },\n]\n\nconst departmentSample = 'Digital NSW, Department of Customer Service'\n\n/** Matches Tailwind's `lg` breakpoint (64rem). Keep the two in step. */\nconst DESKTOP_QUERY = '(min-width: 64rem)'\n\ntype FooterAccordionProps = Omit, 'children'> & {\n columns?: typeof columnsSample\n}\n\n/**\n * A long site map that collapses into accordions on small screens and lays out\n * as plain columns from `lg` up. Use when there are more links than a mobile\n * footer can show without burying the legal row and copyright below a wall of\n * text.\n *\n * How the breakpoint behaviour works, and why it is safe:\n *\n * - Columns render OPEN on the server and on first paint, so the markup is\n * identical on both sides of hydration — no mismatch — and a visitor with\n * JavaScript disabled gets every link, because the collapse only ever\n * happens in an effect.\n * - After mount, a `matchMedia` listener collapses them below `lg` and forces\n * them open at or above it. The disclosure buttons are hidden from `lg` up,\n * so on desktop there is nothing to toggle and nothing extra in the tab\n * order.\n *\n * Base UI's Collapsible owns the `aria-expanded` / `aria-controls` wiring and\n * the keyboard behaviour — the block only decides which columns are open.\n */\nexport function FooterAccordion({\n columns = columnsSample,\n legalLinks = legalLinksSample,\n department = departmentSample,\n ...props\n}: FooterAccordionProps) {\n // Starts `true` so the server render, the pre-hydration paint and the no-JS\n // experience all show the full site map. Never initialise this from\n // `window` — that is what would desync hydration.\n const [isDesktop, setIsDesktop] = React.useState(true)\n const [openColumns, setOpenColumns] = React.useState([])\n\n React.useEffect(() => {\n const query = window.matchMedia(DESKTOP_QUERY)\n const sync = () => setIsDesktop(query.matches)\n\n sync()\n query.addEventListener('change', sync)\n return () => query.removeEventListener('change', sync)\n }, [])\n\n return (\n \n )\n}\n", "type": "registry:component", "target": "components/footer-accordion.tsx" } ], "type": "registry:block", "meta": { "nswdsVersion": "5.1.0" } }