Reactive SVGs: How to Build Illustrations That Respond to Data Using D3 and UK Open Datasets

·

, ,

Raw data is boring. A spreadsheet of ONS broadband penetration figures or Ofcom spectrum usage statistics is about as gripping as a council planning notice. But bind those same numbers to a living, breathing SVG illustration and suddenly you have something people actually want to look at. That is the promise of reactive SVG data visualisation, and D3.js is the library that makes it properly possible without reaching for a bloated charting framework that hides all the interesting bits from you.

This article is a hands-on walkthrough. We are going to fetch real UK open data, parse it, and use D3 to bind it to SVG elements so the visuals respond dynamically to the figures. No toy datasets. No made-up numbers. Real data from sources the UK government and regulators actually publish.

Developer building a reactive SVG data visualisation UK project on a large monitor in a modern studio
Developer building a reactive SVG data visualisation UK project on a large monitor in a modern studio

Why SVG and D3 Instead of Canvas or a Chart Library?

Canvas is fast for pixel-heavy rendering. Chart libraries like Chart.js are quick to deploy. But neither gives you the fine-grained control that reactive SVG data visualisation demands when you want illustrations, not just bar charts. SVG is part of the DOM, which means every shape, path, and text node is queryable, styleable, and animatable with CSS. D3 exploits this completely.

The data join pattern at D3’s core (enter, update, exit) is genuinely elegant once it clicks. You tell D3 what data you have, what DOM elements should represent it, and what to do when data changes. The library handles the rest. It is declarative in the right places and imperative where you need control. I have used it on projects ranging from tiny inline sparklines to full-screen choropleth maps, and I keep coming back because nothing else gives you the same ceiling.

Getting Your UK Open Data: ONS and Ofcom

The Office for National Statistics publishes machine-readable datasets through its ONS API, which returns JSON in a fairly navigable structure. Ofcom releases its Connected Nations datasets as downloadable CSVs covering broadband coverage by local authority district. Both are genuinely free, regularly updated, and ideal for a reactive SVG project.

For this walkthrough we are using Ofcom’s Connected Nations data: specifically, the percentage of premises with access to gigabit-capable broadband by UK nation and region. It is a flat CSV, which means we can use D3’s built-in d3.csv() loader and avoid writing a custom parser.

import * as d3 from "https://cdn.jsdelivr.net/npm/d3@7/+esm";

const DATA_URL = "https://your-hosted-copy/ofcom-connected-nations.csv";

d3.csv(DATA_URL, d => ({
  region: d["Region"],
  gigabit_pct: +d["Gigabit capable (%)"],
  premises: +d["Total premises"]
})).then(data => {
  render(data);
});

Note the + coercion on the numeric fields. D3’s CSV parser returns strings by default. Skip that coercion and your scales will treat “84” as text, which produces spectacularly wrong output. Ask me how I know.

Building the SVG Container and Scales

The margin convention in D3 is worth following even when it feels ceremonial. It keeps your axes and labels inside the viewport without wrestling with overflow: visible hacks later.

const margin = { top: 40, right: 30, bottom: 60, left: 120 };
const width = 800 - margin.left - margin.right;
const height = 500 - margin.top - margin.bottom;

const svg = d3.select("#chart")
  .append("svg")
  .attr("viewBox", `0 0 ${width + margin.left + margin.right} ${height + margin.top + margin.bottom}`)
  .attr("preserveAspectRatio", "xMidYMid meet")
  .append("g")
  .attr("transform", `translate(${margin.left},${margin.top})`);

Using viewBox instead of hard-coded pixel dimensions is non-negotiable if you want the illustration to be responsive. The SVG scales itself to the container, and your D3 coordinates stay consistent regardless of screen size. This is how reactive SVG data visualisation actually becomes reactive to viewport changes as well as data changes.

Close-up of D3.js code powering a reactive SVG data visualisation UK broadband chart on a laptop screen
Close-up of D3.js code powering a reactive SVG data visualisation UK broadband chart on a laptop screen

Now the scales. For a horizontal bar chart of regional broadband coverage:

const x = d3.scaleLinear()
  .domain([0, 100])
  .range([0, width]);

const y = d3.scaleBand()
  .domain(data.map(d => d.region))
  .range([0, height])
  .padding(0.3);

scaleBand handles the spacing maths for categorical axes so you do not have to. The padding value of 0.3 adds 30% whitespace between bands, which visually separates the bars without making them hair-thin.

The D3 Data Join: Where the Reactive Magic Happens

This is the part that confuses people initially and then becomes the thing they explain excitedly to colleagues. The data join binds an array of data objects to a selection of DOM elements. Elements that do not yet exist go into the enter selection. Elements whose data has disappeared go into the exit selection. Everything in between is the update selection.

const bars = svg.selectAll("rect.bar")
  .data(data, d => d.region); // key function keeps transitions smooth

// Enter: create new bars
bars.enter()
  .append("rect")
  .attr("class", "bar")
  .attr("y", d => y(d.region))
  .attr("height", y.bandwidth())
  .attr("x", 0)
  .attr("width", 0) // start at zero for transition
  .attr("fill", "#4F46E5")
  .merge(bars) // merge with update selection
  .transition()
  .duration(800)
  .ease(d3.easeCubicOut)
  .attr("width", d => x(d.gigabit_pct));

// Exit: remove stale bars
bars.exit()
  .transition()
  .duration(400)
  .attr("width", 0)
  .remove();

The key function in .data(data, d => d.region) is critical for smooth transitions. Without it, D3 matches data to elements by index. With it, elements are bound by region name, so if you filter or reorder the dataset the bars animate to their new positions rather than snapping jarringly.

Making It Genuinely Reactive: Filtering and UI Controls

The real payoff of reactive SVG data visualisation comes when you wire up UI controls that change the underlying data and re-call the render function. Add a simple dropdown that filters by UK nation:

const nations = ["All", "England", "Scotland", "Wales", "Northern Ireland"];

const select = d3.select("#controls")
  .append("select")
  .on("change", function() {
    const selected = this.value;
    const filtered = selected === "All"
      ? fullData
      : fullData.filter(d => d.nation === selected);
    render(filtered);
  });

select.selectAll("option")
  .data(nations)
  .enter()
  .append("option")
  .text(d => d);

Each time the user changes the dropdown, render(filtered) runs with a new data array. D3 works out which bars need to be added, which need updating, and which need removing. You do not manually manipulate the DOM. That is the whole point.

Going Beyond Bar Charts: Binding Data to Illustrated SVG Paths

Bar charts are fine, but the really interesting territory is binding data to custom SVG illustrations, like a stylised map of the UK’s twelve regions, or an illustrated diagram of network infrastructure where path stroke-width encodes bandwidth figures.

The principle is identical. Load an SVG file (exported from Figma or Illustrator), inline it in your HTML, then use D3 to select specific paths by their id or data-region attributes and drive their visual properties from data. Colour fills from a sequential scale, opacity driven by coverage percentage, stroke animations that pulse for regions below a threshold: all of it is just attribute binding.

const colour = d3.scaleSequential()
  .domain([0, 100])
  .interpolator(d3.interpolateBlues);

data.forEach(d => {
  d3.select(`#region-${d.region.replace(/\s+/g, "-").toLowerCase()}`)
    .transition()
    .duration(600)
    .attr("fill", colour(d.gigabit_pct));
});

This approach turns a static SVG illustration into a living data artefact. Your designer exports a clean regional map from Figma. Your D3 code breathes data into it. The two disciplines talk to each other through attribute naming conventions agreed upfront. It is the kind of collaboration between design and dev that produces genuinely impressive output.

Performance and Accessibility Notes

A few things worth pinning to your monitor. First, ARIA labels. SVG is not inherently accessible. Add role="img" and aria-label to your SVG container, and use <title> and <desc> elements inside individual groups for screen readers. The Web Accessibility Initiative has clear guidance on SVG accessibility patterns.

Second, transitions look great but they are not free. If you are rendering several hundred elements, consider using d3.transition() with a shared timer rather than individual transitions, and cap your dataset size where the visual encoding stops being legible anyway. A choropleth with 400 regions is just noise.

Third, host your open data files yourself or proxy them. Fetching directly from government data portals in production is fragile; file structures change, URLs break, and there is no SLA. Pull the data into your own infrastructure on a schedule and serve it from there.

The Bottom Line on D3 and Open Data

Reactive SVG data visualisation using UK open datasets is one of those projects that teaches you an enormous amount in a short time. You learn D3’s join model. You learn how SVG coordinate systems actually work. You learn that ONS data is more useful than most developers realise. And you produce something genuinely worth showing in a portfolio, because it combines real public information with craft-level visual thinking. That combination is rare, and it shows.

The Ofcom and ONS datasets are updated regularly, which means a project you build this month stays relevant as new figures land. Wire it up to a scheduled data fetch, add a timestamp to your visualisation, and you have something that essentially maintains itself.

Frequently Asked Questions

What is reactive SVG data visualisation and how does it differ from a static chart?

Reactive SVG data visualisation means your SVG elements update dynamically when the underlying data changes, using a library like D3.js to bind data to DOM attributes in real time. A static chart is a fixed image or pre-rendered output; a reactive one responds to user input, data filters, or live data feeds without a page reload.

Where can I download free UK open datasets to use with D3.js?

The Office for National Statistics (ons.gov.uk) and Ofcom both publish machine-readable open datasets covering topics from broadband coverage to regional demographics. The ONS also provides a developer API that returns JSON, and Ofcom’s Connected Nations data is available as downloadable CSV files updated annually.

Do I need to know D3.js well to build reactive SVG illustrations, or can beginners start here?

D3’s data join pattern has a learning curve, so some JavaScript confidence is recommended before diving in. That said, the enter/update/exit model is well-documented and once it clicks, building reactive SVG visualisations becomes much more intuitive. Starting with a simple bar chart bound to a CSV is the fastest path to understanding it properly.

Can I import an SVG illustration from Figma and bind data to it using D3?

Yes, and this is one of the most powerful workflows available. Export your SVG from Figma, inline it in your HTML, and give key paths meaningful IDs or data attributes. D3 can then select those paths and drive their fill, opacity, stroke, or transform properties directly from your dataset.

How do I make D3 SVG visualisations accessible for screen readers?

Add role=”img” and an aria-label to the SVG container, and include a element as the first child of the SVG for a short description. For more complex visuals, a <desc> element and aria-describedby attribute provide additional context. Avoid encoding critical information solely through colour, and ensure interactive elements are keyboard-navigable.</p> <p><script type="application/ld+json"> { "@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [ { "@type": "Question", "name": "What is reactive SVG data visualisation and how does it differ from a static chart?", "acceptedAnswer": { "@type": "Answer", "text": "Reactive SVG data visualisation means your SVG elements update dynamically when the underlying data changes, using a library like D3.js to bind data to DOM attributes in real time. A static chart is a fixed image or pre-rendered output; a reactive one responds to user input, data filters, or live data feeds without a page reload." } }, { "@type": "Question", "name": "Where can I download free UK open datasets to use with D3.js?", "acceptedAnswer": { "@type": "Answer", "text": "The Office for National Statistics (ons.gov.uk) and Ofcom both publish machine-readable open datasets covering topics from broadband coverage to regional demographics. The ONS also provides a developer API that returns JSON, and Ofcom's Connected Nations data is available as downloadable CSV files updated annually." } }, { "@type": "Question", "name": "Do I need to know D3.js well to build reactive SVG illustrations, or can beginners start here?", "acceptedAnswer": { "@type": "Answer", "text": "D3's data join pattern has a learning curve, so some JavaScript confidence is recommended before diving in. That said, the enter/update/exit model is well-documented and once it clicks, building reactive SVG visualisations becomes much more intuitive. Starting with a simple bar chart bound to a CSV is the fastest path to understanding it properly." } }, { "@type": "Question", "name": "Can I import an SVG illustration from Figma and bind data to it using D3?", "acceptedAnswer": { "@type": "Answer", "text": "Yes, and this is one of the most powerful workflows available. Export your SVG from Figma, inline it in your HTML, and give key paths meaningful IDs or data attributes. D3 can then select those paths and drive their fill, opacity, stroke, or transform properties directly from your dataset." } }, { "@type": "Question", "name": "How do I make D3 SVG visualisations accessible for screen readers?", "acceptedAnswer": { "@type": "Answer", "text": "Add role=\"img\" and an aria-label to the SVG container, and include a element as the first child of the SVG for a short description. For more complex visuals, a element and aria-describedby attribute provide additional context. Avoid encoding critical information solely through colour, and ensure interactive elements are keyboard-navigable." } } ] } </script></p> </div> <div style="height:var(--wp--preset--spacing--40)" aria-hidden="true" class="wp-block-spacer"></div> <div style="font-style:normal;font-weight:400" class="taxonomy-post_tag is-style-post-terms-1 is-style-post-terms-1--2 wp-block-post-terms"><a href="https://launchpad-design.co.uk/tag/d3-js-tutorial/" rel="tag">d3.js tutorial</a><span class="wp-block-post-terms__separator"> </span><a href="https://launchpad-design.co.uk/tag/ofcom-ons-data-visualisation/" rel="tag">ofcom ons data visualisation</a><span class="wp-block-post-terms__separator"> </span><a href="https://launchpad-design.co.uk/tag/reactive-svg-data-visualisation-uk/" rel="tag">reactive svg data visualisation uk</a><span class="wp-block-post-terms__separator"> </span><a href="https://launchpad-design.co.uk/tag/svg-data-binding/" rel="tag">svg data binding</a><span class="wp-block-post-terms__separator"> </span><a href="https://launchpad-design.co.uk/tag/uk-open-data-coding/" rel="tag">uk open data coding</a></div></div> <div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:5%"></div> <div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:25%"><div class="wp-block-template-part"> <h2 class="wp-block-heading has-small-font-size" style="font-style:normal;font-weight:600;letter-spacing:1.6px;text-transform:uppercase">Other Posts</h2> <div style="height:var(--wp--preset--spacing--40)" aria-hidden="true" class="wp-block-spacer"></div> <div class="wp-block-query is-layout-flow wp-block-query-is-layout-flow"> <ul class="wp-block-post-template is-layout-flow wp-block-post-template-is-layout-flow"><li class="wp-block-post post-204 post type-post status-publish format-standard has-post-thumbnail hentry category-coding category-design category-web-design tag-bento-grid-css tag-bento-grid-ui-design-uk tag-css-grid-layout tag-saas-ui-design tag-web-design-trends-2026"> <div class="wp-block-group is-vertical is-layout-flex wp-container-core-group-is-layout-54d079fe wp-block-group-is-layout-flex"> <h2 class="wp-block-post-title has-medium-font-size"><a href="https://launchpad-design.co.uk/bento-grid-layouts-ui-trend-british-saas/" target="_self" >Bento Grid Layouts: The UI Trend Redefining How British SaaS Products Present Features</a></h2> <div class="wp-block-post-date has-small-font-size"><a href="https://launchpad-design.co.uk/bento-grid-layouts-ui-trend-british-saas/"><time datetime="2026-07-23T22:42:44+00:00">July 23, 2026</time></a></div> </div> <div style="height:var(--wp--preset--spacing--20)" aria-hidden="true" class="wp-block-spacer"></div> </li><li class="wp-block-post post-201 post type-post status-publish format-standard has-post-thumbnail hentry category-coding category-nerdy category-web-design tag-d3-js-tutorial tag-ofcom-ons-data-visualisation tag-reactive-svg-data-visualisation-uk tag-svg-data-binding tag-uk-open-data-coding"> <div class="wp-block-group is-vertical is-layout-flex wp-container-core-group-is-layout-54d079fe wp-block-group-is-layout-flex"> <h2 class="wp-block-post-title has-medium-font-size"><a href="https://launchpad-design.co.uk/reactive-svg-data-visualisation-uk-d3-open-data/" target="_self" >Reactive SVGs: How to Build Illustrations That Respond to Data Using D3 and UK Open Datasets</a></h2> <div class="wp-block-post-date has-small-font-size"><a href="https://launchpad-design.co.uk/reactive-svg-data-visualisation-uk-d3-open-data/"><time datetime="2026-07-23T21:03:20+00:00">July 23, 2026</time></a></div> </div> <div style="height:var(--wp--preset--spacing--20)" aria-hidden="true" class="wp-block-spacer"></div> </li><li class="wp-block-post post-198 post type-post status-publish format-standard has-post-thumbnail hentry category-design category-nerdy category-web-design tag-fca-consumer-duty-design tag-financial-promotion-rules-ui tag-fintech-ux-design-compliance tag-regulatory-ux-design tag-uk-fintech-product-design"> <div class="wp-block-group is-vertical is-layout-flex wp-container-core-group-is-layout-54d079fe wp-block-group-is-layout-flex"> <h2 class="wp-block-post-title has-medium-font-size"><a href="https://launchpad-design.co.uk/designing-fintech-interfaces-fca-regulatory-compliance-uk/" target="_self" >Designing Fintech Interfaces for UK Regulatory Compliance: FCA Rules Every Product Designer Should Know</a></h2> <div class="wp-block-post-date has-small-font-size"><a href="https://launchpad-design.co.uk/designing-fintech-interfaces-fca-regulatory-compliance-uk/"><time datetime="2026-07-17T09:43:56+00:00">July 17, 2026</time></a></div> </div> <div style="height:var(--wp--preset--spacing--20)" aria-hidden="true" class="wp-block-spacer"></div> </li><li class="wp-block-post post-195 post type-post status-publish format-standard has-post-thumbnail hentry category-coding category-web-design tag-css-grid-guide tag-css-grid-vs-flexbox-2026 tag-css-layout-systems tag-flexbox-tutorial tag-front-end-development"> <div class="wp-block-group is-vertical is-layout-flex wp-container-core-group-is-layout-54d079fe wp-block-group-is-layout-flex"> <h2 class="wp-block-post-title has-medium-font-size"><a href="https://launchpad-design.co.uk/css-grid-vs-flexbox-2026/" target="_self" >CSS Grid vs Flexbox in 2026: Which Layout System Should You Actually Use?</a></h2> <div class="wp-block-post-date has-small-font-size"><a href="https://launchpad-design.co.uk/css-grid-vs-flexbox-2026/"><time datetime="2026-06-30T18:05:11+00:00">June 30, 2026</time></a></div> </div> <div style="height:var(--wp--preset--spacing--20)" aria-hidden="true" class="wp-block-spacer"></div> </li></ul> </div> </div></div> <div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:5%"></div> </div> </div> <div class="wp-block-group alignwide has-global-padding is-layout-constrained wp-block-group-is-layout-constrained" style="margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--60)"> <nav class="wp-block-group alignwide is-content-justification-space-between is-nowrap is-layout-flex wp-container-core-group-is-layout-878fe601 wp-block-group-is-layout-flex" aria-label="Post navigation" style="border-top-color:var(--wp--preset--color--accent-6);border-top-width:1px;padding-top:var(--wp--preset--spacing--40);padding-bottom:var(--wp--preset--spacing--40)"><div class="post-navigation-link-previous wp-block-post-navigation-link"><span class="wp-block-post-navigation-link__arrow-previous is-arrow-arrow" aria-hidden="true">←</span><a href="https://launchpad-design.co.uk/designing-fintech-interfaces-fca-regulatory-compliance-uk/" rel="prev">Designing Fintech Interfaces for UK Regulatory Compliance: FCA Rules Every Product Designer Should Know</a></div> <div class="post-navigation-link-next wp-block-post-navigation-link"><a href="https://launchpad-design.co.uk/bento-grid-layouts-ui-trend-british-saas/" rel="next">Bento Grid Layouts: The UI Trend Redefining How British SaaS Products Present Features</a><span class="wp-block-post-navigation-link__arrow-next is-arrow-arrow" aria-hidden="true">→</span></div></nav> </div> <div class="wp-block-group alignwide has-global-padding is-content-justification-center is-layout-constrained wp-block-group-is-layout-constrained"> <div class="wp-block-columns alignwide is-layout-flex wp-container-core-columns-is-layout-8599fcd2 wp-block-columns-is-layout-flex" style="margin-top:0;margin-bottom:0"> <div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:5%"></div> <div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="padding-top:0;padding-bottom:0;flex-basis:65%"> <div class="wp-block-group is-layout-flow wp-block-group-is-layout-flow" style="padding-top:0;padding-right:0;padding-bottom:0;padding-left:0"> <div class="wp-block-comments wp-block-comments-query-loop" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--70)"> <h2 class="wp-block-heading has-x-large-font-size">Comments</h2> <div id="respond" class="comment-respond wp-block-post-comments-form"> <h3 id="reply-title" class="comment-reply-title">Leave a Reply <small><a rel="nofollow" id="cancel-comment-reply-link" href="/reactive-svg-data-visualisation-uk-d3-open-data/#respond" style="display:none;">Cancel reply</a></small></h3><form action="https://launchpad-design.co.uk/wp-comments-post.php" method="post" id="commentform" class="comment-form"><p class="comment-notes"><span id="email-notes">Your email address will not be published.</span> <span class="required-field-message">Required fields are marked <span class="required">*</span></span></p><p class="comment-form-comment"><label for="comment">Comment <span class="required">*</span></label> <textarea id="comment" name="comment" cols="45" rows="8" maxlength="65525" required></textarea></p><p class="comment-form-author"><label for="author">Name <span class="required">*</span></label> <input id="author" name="author" type="text" value="" size="30" maxlength="245" autocomplete="name" required /></p> <p class="comment-form-email"><label for="email">Email <span class="required">*</span></label> <input id="email" name="email" type="email" value="" size="30" maxlength="100" aria-describedby="email-notes" autocomplete="email" required /></p> <p class="comment-form-url"><label for="url">Website</label> <input id="url" name="url" type="url" value="" size="30" maxlength="200" autocomplete="url" /></p> <p class="comment-form-cookies-consent"><input id="wp-comment-cookies-consent" name="wp-comment-cookies-consent" type="checkbox" value="yes" /> <label for="wp-comment-cookies-consent">Save my name, email, and website in this browser for the next time I comment.</label></p> <p class="form-submit wp-block-button"><span id="cf-turnstile-c-3817182468" class="cf-turnstile cf-turnstile-comments" data-action="wordpress-comment" data-callback="" data-sitekey="0x4AAAAAACb4GW2MZsmQ-tH0" data-theme="light" data-language="auto" data-appearance="always" data-size="normal" data-retry="auto" data-retry-interval="1000"></span><br class="cf-turnstile-br cf-turnstile-br-comments"><input name="submit" type="submit" id="submit" class="wp-block-button__link wp-element-button" value="Post Comment" /><script type="text/javascript">document.addEventListener("DOMContentLoaded", function() { document.body.addEventListener("click", function(event) { if (event.target.matches(".comment-reply-link, #cancel-comment-reply-link")) { turnstile.reset(".comment-form .cf-turnstile"); } }); });</script> <input type='hidden' name='comment_post_ID' value='201' id='comment_post_ID' /> <input type='hidden' name='comment_parent' id='comment_parent' value='0' /> </p></form> </div><!-- #respond --> </div> </div> </div> <div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:5%"></div> <div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:25%"></div> <div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:5%"></div> </div> </div> </main> <footer class="wp-block-template-part"> <div class="wp-block-group alignfull is-style-section-4 has-contrast-color has-base-background-color has-text-color has-background has-link-color wp-elements-32359b7246c3809d18254618840e7f33 has-global-padding is-layout-constrained wp-container-core-group-is-layout-58f2d333 wp-block-group-is-layout-constrained is-style-section-4--3" style="padding-top:var(--wp--preset--spacing--50);padding-right:var(--wp--preset--spacing--40);padding-bottom:var(--wp--preset--spacing--50);padding-left:var(--wp--preset--spacing--40)"> <div class="wp-block-group alignwide is-content-justification-space-between is-layout-flex wp-container-core-group-is-layout-686a51e3 wp-block-group-is-layout-flex"> <div class="wp-block-group wp-container-content-9cfa9a5a has-global-padding is-content-justification-left is-layout-constrained wp-container-core-group-is-layout-236ffaf5 wp-block-group-is-layout-constrained"> <p class="has-text-align-left wp-block-paragraph" style="font-size:clamp(0.875rem, 0.875rem + ((1vw - 0.2rem) * 0.039), 0.9rem);font-style:normal;font-weight:600;letter-spacing:1px;text-transform:uppercase">Search</p> <form role="search" method="get" action="https://launchpad-design.co.uk/" class="wp-block-search__button-outside wp-block-search__icon-button wp-block-search" ><label class="wp-block-search__label screen-reader-text" for="wp-block-search__input-4" >Search</label><div class="wp-block-search__inside-wrapper" style="width: 100%"><input class="wp-block-search__input" id="wp-block-search__input-4" placeholder="" value="" type="search" name="s" required style="border-width: 1px"/><button aria-label="Search" class="wp-block-search__button has-background has-icon wp-element-button" type="submit" style="border-width: 1px;background-color: #f3931d"><svg class="search-icon" viewBox="0 0 24 24" width="24" height="24"> <path d="M13 5c-3.3 0-6 2.7-6 6 0 1.4.5 2.7 1.3 3.7l-3.8 3.8 1.1 1.1 3.8-3.8c1 .8 2.3 1.3 3.7 1.3 3.3 0 6-2.7 6-6S16.3 5 13 5zm0 10.5c-2.5 0-4.5-2-4.5-4.5s2-4.5 4.5-4.5 4.5 2 4.5 4.5-2 4.5-4.5 4.5z"></path> </svg></button></div></form></div> </div> <div style="height:48px" aria-hidden="true" class="wp-block-spacer"></div> <div class="wp-block-group alignwide is-content-justification-space-between is-layout-flex wp-container-core-group-is-layout-1d0a7695 wp-block-group-is-layout-flex"> <div class="wp-block-group is-layout-flex wp-block-group-is-layout-flex"><div class="is-default-size wp-block-site-logo"><a href="https://launchpad-design.co.uk/" class="custom-logo-link" rel="home"><img loading="lazy" width="731" height="279" src="https://launchpad-design.co.uk/wp-content/uploads/2026/01/launchpad_logo.jpg" class="custom-logo" alt="Launchpad Design news and articles" decoding="async" srcset="https://launchpad-design.co.uk/wp-content/uploads/2026/01/launchpad_logo.jpg 731w, https://launchpad-design.co.uk/wp-content/uploads/2026/01/launchpad_logo-300x115.jpg 300w" sizes="auto, (max-width: 731px) 100vw, 731px" /></a></div></div> <p class="has-small-font-size wp-block-paragraph">Managed by <a href="https://inkd.uk" target="_blank" rel="noreferrer noopener nofollow">Inkd</a></p> </div> </div> </footer></div> <script type="speculationrules"> {"prefetch":[{"source":"document","where":{"and":[{"href_matches":"/*"},{"not":{"href_matches":["/wp-*.php","/wp-admin/*","/wp-content/uploads/*","/wp-content/*","/wp-content/plugins/*","/wp-content/themes/twentytwentyfive/*","/*\\?(.+)"]}},{"not":{"selector_matches":"a[rel~=\"nofollow\"]"}},{"not":{"selector_matches":".no-prefetch, .no-prefetch a"}}]},"eagerness":"conservative"}]} </script> <script data-wp-strategy="defer" id="cfturnstile-js" src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=auto"></script> <script data-wp-strategy="defer" defer id="cfturnstile-js-js" src="https://launchpad-design.co.uk/wp-content/plugins/simple-cloudflare-turnstile/js/disable-submit.js?ver=5.2"></script> <script id="cfturnstile-render-js-after"> (function(){var a=0,d=false,q=false;function r(){if(d)return;var e=document.getElementById("cf-turnstile-c-3817182468");if(!e)return;if(e.innerHTML.trim()){d=true;return;}if(window.turnstile&&typeof window.turnstile.render==="function"){try{window.turnstile.render(e);d=true;}catch(_){}}}function w(){r();if(d)return;if(window.turnstile&&typeof window.turnstile.ready==="function"&&!q){q=true;try{window.turnstile.ready(r);}catch(_){}}if(!d&&a++<100){setTimeout(w,100);}}if(document.readyState==="loading"){document.addEventListener("DOMContentLoaded",w);}else{w();}})(); //# sourceURL=cfturnstile-render-js-after </script> <script async data-wp-strategy="async" fetchpriority="low" id="comment-reply-js" src="https://launchpad-design.co.uk/wp-includes/js/comment-reply.min.js?ver=7.0.2"></script> <script id="eztoc-scroll-scriptjs-js-extra"> var eztoc_smooth_local = {"scroll_offset":"30","add_request_uri":"","add_self_reference_link":""}; //# sourceURL=eztoc-scroll-scriptjs-js-extra </script> <script id="eztoc-scroll-scriptjs-js" src="https://launchpad-design.co.uk/wp-content/plugins/easy-table-of-contents/assets/js/smooth_scroll.min.js?ver=2.0.85"></script> <script id="eztoc-js-cookie-js" src="https://launchpad-design.co.uk/wp-content/plugins/easy-table-of-contents/vendor/js-cookie/js.cookie.min.js?ver=2.2.1"></script> <script id="eztoc-jquery-sticky-kit-js" src="https://launchpad-design.co.uk/wp-content/plugins/easy-table-of-contents/vendor/sticky-kit/jquery.sticky-kit.min.js?ver=1.9.2"></script> <script id="eztoc-js-js-extra"> var ezTOC = {"smooth_scroll":"1","visibility_hide_by_default":"","scroll_offset":"30","fallbackIcon":"\u003Cspan class=\"\"\u003E\u003Cspan class=\"eztoc-hide\" style=\"display:none;\"\u003EToggle\u003C/span\u003E\u003Cspan class=\"ez-toc-icon-toggle-span\"\u003E\u003Csvg style=\"fill: #999;color:#999\" xmlns=\"http://www.w3.org/2000/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"\u003E\u003Cpath d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"\u003E\u003C/path\u003E\u003C/svg\u003E\u003Csvg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http://www.w3.org/2000/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"\u003E\u003Cpath d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"/\u003E\u003C/svg\u003E\u003C/span\u003E\u003C/span\u003E","chamomile_theme_is_on":""}; //# sourceURL=eztoc-js-js-extra </script> <script id="eztoc-js-js" src="https://launchpad-design.co.uk/wp-content/plugins/easy-table-of-contents/assets/js/front.min.js?ver=2.0.85-1781248131"></script> <script id="wp-emoji-settings" type="application/json"> {"baseUrl":"https://s.w.org/images/core/emoji/17.0.2/72x72/","ext":".png","svgUrl":"https://s.w.org/images/core/emoji/17.0.2/svg/","svgExt":".svg","source":{"concatemoji":"https://launchpad-design.co.uk/wp-includes/js/wp-emoji-release.min.js?ver=7.0.2"}} </script> <script type="module"> /*! This file is auto-generated */ const a=JSON.parse(document.getElementById("wp-emoji-settings").textContent),o=(window._wpemojiSettings=a,"wpEmojiSettingsSupports"),s=["flag","emoji"];function i(e){try{var t={supportTests:e,timestamp:(new Date).valueOf()};sessionStorage.setItem(o,JSON.stringify(t))}catch(e){}}function c(e,t,n){e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(t,0,0);t=new Uint32Array(e.getImageData(0,0,e.canvas.width,e.canvas.height).data);e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(n,0,0);const a=new Uint32Array(e.getImageData(0,0,e.canvas.width,e.canvas.height).data);return t.every((e,t)=>e===a[t])}function p(e,t){e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(t,0,0);var n=e.getImageData(16,16,1,1);for(let e=0;e<n.data.length;e++)if(0!==n.data[e])return!1;return!0}function u(e,t,n,a){switch(t){case"flag":return n(e,"\ud83c\udff3\ufe0f\u200d\u26a7\ufe0f","\ud83c\udff3\ufe0f\u200b\u26a7\ufe0f")?!1:!n(e,"\ud83c\udde8\ud83c\uddf6","\ud83c\udde8\u200b\ud83c\uddf6")&&!n(e,"\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc65\udb40\udc6e\udb40\udc67\udb40\udc7f","\ud83c\udff4\u200b\udb40\udc67\u200b\udb40\udc62\u200b\udb40\udc65\u200b\udb40\udc6e\u200b\udb40\udc67\u200b\udb40\udc7f");case"emoji":return!a(e,"\ud83e\u1fac8")}return!1}function f(e,t,n,a){let r;const o=(r="undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?new OffscreenCanvas(300,150):document.createElement("canvas")).getContext("2d",{willReadFrequently:!0}),s=(o.textBaseline="top",o.font="600 32px Arial",{});return e.forEach(e=>{s[e]=t(o,e,n,a)}),s}function r(e){var t=document.createElement("script");t.src=e,t.defer=!0,document.head.appendChild(t)}a.supports={everything:!0,everythingExceptFlag:!0},new Promise(t=>{let n=function(){try{var e=JSON.parse(sessionStorage.getItem(o));if("object"==typeof e&&"number"==typeof e.timestamp&&(new Date).valueOf()<e.timestamp+604800&&"object"==typeof e.supportTests)return e.supportTests}catch(e){}return null}();if(!n){if("undefined"!=typeof Worker&&"undefined"!=typeof OffscreenCanvas&&"undefined"!=typeof URL&&URL.createObjectURL&&"undefined"!=typeof Blob)try{var e="postMessage("+f.toString()+"("+[JSON.stringify(s),u.toString(),c.toString(),p.toString()].join(",")+"));",a=new Blob([e],{type:"text/javascript"});const r=new Worker(URL.createObjectURL(a),{name:"wpTestEmojiSupports"});return void(r.onmessage=e=>{i(n=e.data),r.terminate(),t(n)})}catch(e){}i(n=f(s,u,c,p))}t(n)}).then(e=>{for(const n in e)a.supports[n]=e[n],a.supports.everything=a.supports.everything&&a.supports[n],"flag"!==n&&(a.supports.everythingExceptFlag=a.supports.everythingExceptFlag&&a.supports[n]);var t;a.supports.everythingExceptFlag=a.supports.everythingExceptFlag&&!a.supports.flag,a.supports.everything||((t=a.source||{}).concatemoji?r(t.concatemoji):t.wpemoji&&t.twemoji&&(r(t.twemoji),r(t.wpemoji)))}); //# sourceURL=https://launchpad-design.co.uk/wp-includes/js/wp-emoji-loader.min.js </script> </body> </html>