Skip to content
Rhodie

Integration Toolkit: Family for Every Child


2025SvelteKit, Svelte 5, TypeScript, Tailwind CSS 4, Cloudflare Pages

Integration Toolkit: Family for Every Child

2025 · SvelteKit, Svelte 5, TypeScript, Tailwind CSS 4, Cloudflare Pages

About This Project

Family for Every Child is a UK-registered international children's charity. One of their projects, funded by Comic Relief, produced a major report on supporting the integration of children and young people on the move. The report was originally published as a PDF, but it was large, dense with valuable content, and difficult to navigate on a phone. The charity wanted to get it online as a standalone microsite where individual sections could be shared, updated over time, and read properly on any device.

The first version was built in WordPress. It worked, but the site is accessed globally and often on unreliable connections. I wanted to push the performance further than WordPress could comfortably go. The goal was to get as close to 100/100 across the board on Google PageSpeed Insights, including on mobile.

Moving to SvelteKit with a static adapter got us there. As of today, the site scores 100 for Performance, 95 for Accessibility, 100 for Best Practices, and 100 for SEO on mobile. That's not a controlled test on a single page. That's the real score on a content-heavy site with images, interactive components, and multilingual PDF downloads.

Design Approach

The original PDF was structured around a "house model" of integration with six dimensions: Foundations, Autonomy, Personal Capacities, Environment, Social Connections, and Sustainability. The challenge was turning a linear document into something that felt navigable without losing the structure that practitioners were already familiar with.

The layout uses a persistent sidebar on desktop with the full navigation visible at all times. Each of the six dimensions has its own icon in the sidebar, so returning users can jump straight to the section they need. On mobile, this collapses into a hamburger menu with accordion-style sections that expand to show sub-pages. The sidebar auto-expands to show where you are in the hierarchy when you land on a page, so you always have context.

The homepage needed to communicate the scale of the issue quickly. There are animated counters that roll up to 36 million (the number of children living outside their country of birth) and a character-by-character reveal of "1/3" (the proportion who are refugees). These aren't decorative. They're the first thing a policymaker or funder sees, and they set the tone for why the toolkit exists.

Each section of the toolkit gets a layout that fits its content. The guiding principles page uses an accordion with numbered icons, so practitioners can scan twelve principles without being overwhelmed by a wall of text. The integration model page has an interactive house diagram with hotspot overlays on each dimension. On desktop, hovering a hotspot shows a tooltip and clicking navigates to that dimension. On mobile, tapping shows the tooltip with a "View More" link, and the tooltip positioning adapts based on where the hotspot sits on screen to make sure it doesn't overflow.

The voices of children page is different again. It's a photo gallery of artwork and messages from migrant children in Italy and Greece. The images are displayed with slight random rotations and vertical stagger to give it the feeling of photos pinned to a board rather than a rigid grid. As you scroll, the rotation of each card shifts slightly in response to its position on screen, and hovering snaps a card to level and scales it up. It's a small touch, but it gives the page a warmth that a standard image grid wouldn't have.

The downloadable PDFs are available in seven languages (English, Spanish, Bulgarian, French, German, Greek, and Italian), served from Cloudflare R2 storage. Each language gets a card with its flag emoji, and the cards have a hover effect that transitions from white to the brand blue.

The colour palette is deliberately restrained. A deep blue (#005499) as the primary brand colour, a gold (#edb100) for accents and calls to action, and a warm cream for content backgrounds. It's a charity site aimed at practitioners, so the design needed to feel trustworthy and calm without being sterile.

The Migration

The WordPress site had dozens of pages organised in a deep hierarchy. Recreating that structure manually in SvelteKit would have been tedious and error-prone, so I wrote a script that automates it. It fetches the live WordPress sitemap XML, parses every URL, filters out non-page resources (feeds, wp-json endpoints, media files), and generates the entire SvelteKit project structure automatically:

async function main() {
  // Fetch and parse the live WordPress sitemap
  const res = await fetch(sitemapUrl);
  const xml = await res.text();
  const data = new XMLParser({ ignoreAttributes: false }).parse(xml);
  const urls = (data.urlset?.url || [])
    .map((u) => (typeof u.loc === 'string' ? u.loc : u.loc?.['#text']))
    .filter(Boolean)
    .filter((u) => !isExcluded(u));

  // Create a markdown file with frontmatter for every page
  for (const p of paths) {
    const dir = path.join(contentRoot, ...pathToSegments(p));
    await fs.mkdir(dir, { recursive: true });
    const weight = (siblings.indexOf(p) + 1) * 10;
    await fs.writeFile(path.join(dir, 'index.md'), frontmatter, 'utf8');
  }

  // Build a typed navigation tree from the URL hierarchy
  // and output it as a TypeScript file with a NavItem interface
  sortTree(navRoot);
  await fs.writeFile(
    path.join(navTsPath, 'navigation.ts'),
    navTs,
    'utf8'
  );
}

One command, and you go from a live WordPress sitemap to a fully scaffolded SvelteKit project with routing, content placeholders, and a typed navigation tree ready to go. The content could then be migrated page by page without worrying about getting the structure wrong.

Content Architecture

On the SvelteKit side, a single catch-all route handles every content page. It reads the markdown file that matches the URL, parses the frontmatter, and renders the content. The page component checks which page it's on and applies the right layout: the guiding principles page gets an accordion, the integration model gets the house diagram, the voices page gets the photo gallery, and everything else gets standard prose with an auto-generated table of contents.

This means the charity team can update content by editing markdown files and pushing to the repository. No WordPress login, no database, no plugins to update, no security patches. Just text files and a deploy.

What I Learned

This was my first Svelte project, and I chose it specifically because the use case suited it. A content-heavy site that needs to be fast, accessible, and low-maintenance. SvelteKit with the static adapter compiles everything to plain HTML at build time, which means there's essentially no JavaScript overhead on pages that don't need it. The framework disappears.

The scaffold script turned what would have been a full day of manual work into a single command. And because it reads from the live sitemap, it could be rerun any time the WordPress site structure changed during the transition period. That was the kind of practical engineering decision that made the whole project smoother.

WP Portfolio