Category: Coding

  • Why Your Website’s Core Web Vitals Are Still Broken in 2026 (And How to Actually Fix Them)

    Why Your Website’s Core Web Vitals Are Still Broken in 2026 (And How to Actually Fix Them)

    Right. You’ve run PageSpeed Insights, stared at a wall of amber and red scores, and muttered something unprintable at your screen. Welcome to the club. Despite Google making Core Web Vitals a ranking signal years ago, a staggering proportion of UK small business and e-commerce sites are still failing at least one metric. According to the ONS data on UK internet industry, the number of UK businesses trading online continues to grow, which makes it all the more baffling that so many of them are haemorrhaging rankings because of fixable performance issues. This is your technically grounded guide to the core web vitals fix your UK website actually needs in 2026.

    Developer analysing core web vitals fix for a UK website in 2026 on multiple monitors
    Developer analysing core web vitals fix for a UK website in 2026 on multiple monitors

    What Are Core Web Vitals and Why Do They Still Matter in 2026?

    Three metrics. That’s all Google is officially measuring under Core Web Vitals: Largest Contentful Paint (LCP), Interaction to Next Paint (INP), and Cumulative Layout Shift (CLS). INP replaced First Input Delay in early 2024 and it’s been quietly brutalising sites ever since. These aren’t abstract benchmarks invented by committee; they map directly to how a real human being experiences loading a page on a 4G connection on the Tube.

    LCP measures how fast your biggest visible element renders. INP measures how snappy your site feels when someone taps, clicks, or types. CLS measures whether your page jumps around like a nervous ferret while it loads. Fail any of them and you’re not just annoying your users; you’re handing a quiet ranking penalty to competitors who bothered to sort theirs out.

    Why UK SME and E-Commerce Sites Fail More Than They Should

    I’ve looked at a lot of UK business sites over the years and the failure patterns are almost always the same. It’s rarely one catastrophic problem. It’s death by a thousand cuts: a bloated WooCommerce theme, render-blocking Google Tag Manager scripts, hero images served without modern compression, third-party chat widgets loading synchronously. Each one adds a few hundred milliseconds. Collectively they torpedo your LCP.

    UK e-commerce sites in particular tend to inherit technical debt from theme marketplaces. Themes built on Bootstrap 4, autoloading twelve Google Fonts variants, carousels powered by jQuery plugins from 2019. The stacking effect is brutal. A site that looks fine on a developer’s M3 MacBook Pro over fibre will absolutely fall apart for someone browsing on an iPhone SE in a post office queue in Wolverhampton.

    Fixing LCP: The Largest Contentful Paint Problem

    Your LCP target is under 2.5 seconds. Most failing UK sites are sitting between 3.5 and 6 seconds. The biggest culprits are almost always images and render-blocking resources.

    Start with your hero image. If it’s a JPEG or PNG being loaded via a CSS background, that’s two problems at once. Switch to WebP or AVIF (AVIF compression is genuinely remarkable at this point), serve it as an <img> element with proper dimensions declared, and add fetchpriority="high" to the tag. That single attribute tells the browser this image is critical and to fetch it immediately rather than queuing it behind other resources.

    <img
      src="hero.avif"
      width="1200"
      height="630"
      fetchpriority="high"
      alt="Your descriptive alt text"
    >

    Next: preload your LCP image in the <head>. This is still criminally underused on UK sites.

    <link rel="preload" as="image" href="hero.avif" fetchpriority="high">

    Finally, audit your render-blocking scripts. Google Tag Manager firing synchronously in the <head> is an LCP killer. Move third-party scripts to load with defer or async wherever possible. GTM itself should load asynchronously; if it isn’t, something has gone wrong with your implementation.

    Chrome DevTools performance panel showing long tasks relevant to core web vitals fix
    Chrome DevTools performance panel showing long tasks relevant to core web vitals fix

    Fixing INP: Interaction to Next Paint Is the Hard One

    INP is the metric that’s caught the most sites off-guard since it replaced FID. The threshold for a good score is under 200 milliseconds. Poor is anything over 500ms. The nuance here is that INP measures the worst interaction across an entire page session, not just the first one. That means a sluggish dropdown menu or a heavy on-click handler buried in a product filter can tank your entire score.

    The main culprit for high INP on UK e-commerce sites is long tasks on the main thread. JavaScript that runs for more than 50ms without yielding blocks the browser from responding to user input. Here’s the pattern to break it up:

    // Instead of one long synchronous function:
    function heavyTask() {
      // ...200ms of work...
    }
    
    // Yield to the browser between chunks:
    async function yieldingTask() {
      for (const chunk of dataChunks) {
        processChunk(chunk);
        await new Promise(resolve => setTimeout(resolve, 0));
      }
    }

    The scheduler.postTask() API is worth exploring if you’re on a modern stack; it gives you fine-grained control over task priority. For WordPress and WooCommerce sites, the quickest win is usually auditing which plugins are registering event listeners on every page. WooCommerce cart fragments, live chat scripts, cookie consent managers; each one adds JavaScript weight that the browser has to process before it can respond to the next click.

    Use Chrome DevTools’ Performance panel (or the slightly more accessible Web Vitals extension) to identify which interactions are generating the longest tasks. Look for the red triangles. Then work backwards to the script responsible.

    Fixing CLS: Stop Your Page Jumping Around

    Cumulative Layout Shift should be under 0.1. It’s the most visually obvious failure and often the easiest to fix, yet plenty of UK sites are still shipping CLS scores of 0.3 or worse.

    The classic cause: images without declared dimensions. When the browser doesn’t know how tall an image is before it loads, it reserves no space. Then the image appears and shunts everything down the page. The fix is a single line of CSS that’s been good practice for years but somehow still gets skipped:

    img, video {
      aspect-ratio: attr(width) / attr(height);
      height: auto;
      width: 100%;
    }

    Always declare explicit width and height attributes on your <img> tags too. The browser uses these to calculate space before the image loads.

    Web fonts are the other sneaky CLS source. When your custom font loads and swaps in, text reflows and shifts the layout. The fix is font-display: optional for non-critical fonts, or font-display: swap combined with a closely matched system font fallback using the size-adjust CSS descriptor. The font matching tools from Malte Ubl’s Fontaine project are genuinely useful here for generating fallback metrics automatically.

    Ad slots, banners, and dynamically injected content are the third category. Reserve space for them explicitly with CSS. A banner that loads after the DOM has painted and pushes your content down by 60 pixels will absolutely destroy your CLS score.

    Measuring the Right Way: Real User Data vs Lab Data

    PageSpeed Insights shows you two sets of data: lab data (simulated, consistent, useful for debugging) and field data from the Chrome User Experience Report (CrUX). Google’s ranking decisions are based on CrUX field data, not lab scores. A site can score 95 in PageSpeed lab conditions and still fail Core Web Vitals in the field if real users on real networks and devices are having a different experience.

    If your site doesn’t yet have enough traffic to appear in CrUX, you’re assessed at the origin level or not at all. But you should still optimise; you’re building the performance foundation for when the data does accumulate. Use the Google Search Console Core Web Vitals report to see page-group level field data for your actual UK users.

    The bottom line for a core web vitals fix on a UK website in 2026 is this: it’s almost never one thing. It’s the compound effect of images, scripts, fonts, and layout choices that were each fine in isolation but terrible together. Audit methodically, fix the highest-impact items first (LCP image delivery and render-blocking scripts will move the needle fastest), and measure with field data, not just lab scores. Your rankings, and your users, will thank you for it.

    Frequently Asked Questions

    What is a good Core Web Vitals score in 2026?

    Google defines ‘good’ as LCP under 2.5 seconds, INP under 200 milliseconds, and CLS under 0.1. All three thresholds need to be met at the 75th percentile of real user page loads to pass. Hitting two out of three still counts as a partial failure.

    How do I check my Core Web Vitals for free?

    Google Search Console gives you real-user field data grouped by page type, which is the most valuable report for UK site owners. PageSpeed Insights (pagespeed.web.dev) gives you both lab and field data for individual URLs. The Chrome Web Vitals browser extension lets you measure in real time as you browse.

    Does fixing Core Web Vitals actually improve Google rankings?

    Yes, though it’s one signal among many. Google uses Core Web Vitals as a tiebreaker when content quality is broadly similar between competing pages. For competitive UK e-commerce and local search queries, the difference between a passing and failing score can visibly shift rankings. More importantly, faster sites convert better.

    Why is my WordPress site failing INP?

    WordPress and WooCommerce sites typically accumulate JavaScript from multiple plugins all registering event listeners and running tasks on the main thread simultaneously. Cart fragment scripts, live chat widgets, cookie consent managers, and page builder scripts are common culprits. Audit your loaded scripts with Chrome DevTools and remove or defer anything not critical to the initial interaction.

    How long does it take to fix Core Web Vitals?

    Technical fixes can be implemented in a day or two for a developer who knows what they’re looking for. However, Google’s CrUX field data updates on a rolling 28-day window, so you won’t see improvements reflected in Search Console immediately. Expect three to four weeks before field data catches up to your fixes.

  • Variable Fonts in 2026: The Typography Superpower Most Sites Aren’t Using

    Variable Fonts in 2026: The Typography Superpower Most Sites Aren’t Using

    Typography on the web has always been a bit of a faff. You pick a typeface, you download four or five separate font files for the different weights and styles, your page load bloats accordingly, and then your designer asks for a slightly bolder heading variant and the whole cycle starts again. Variable fonts break that cycle completely. And yet, despite browser support being essentially universal since around 2020, a surprising number of live production sites are still serving static font stacks like it’s 2015. In variable fonts web design, there is a genuinely dramatic performance and flexibility win sitting on the table, and most teams still haven’t picked it up.

    Monitor displaying variable fonts web design weight axis specimens in a modern studio
    Monitor displaying variable fonts web design weight axis specimens in a modern studio

    What Are Variable Fonts, Exactly?

    A variable font is a single font file that contains an entire design space rather than a fixed snapshot. Instead of separate files for Regular, Medium, SemiBold, Bold, ExtraBold and so on, you get one file with axes that you can interpolate along continuously. The OpenType variable font specification defines several standard axes: wght (weight), wdth (width), ital (italic), slnt (slant), and opsz (optical size). Typeface designers can also define custom axes, which opens up some genuinely wild creative territory. Recursive, a variable font from ArrowType, has an axis called MONO that lets you slide between proportional and monospaced spacing mid-render. That kind of thing simply does not exist in the static font world.

    The spec is maintained by the OpenType consortium and has been supported in Chrome, Firefox, Safari and Edge for years. The Google web.dev documentation on variable fonts is still one of the clearest technical references going, even if you want to go deeper than this article covers.

    Why Variable Fonts Are a Real Performance Win

    Here is the part that tends to surprise people. A single variable font file is not the same size as all those individual static files added together. It is considerably smaller. A typical type family might ship five or six static weight files totalling 300-400 KB combined. The equivalent variable font file frequently comes in under 100 KB, sometimes much less depending on the character set. That is a meaningful reduction in font payload, and on mobile connections, particularly on 4G in rural areas of the UK where speeds can be inconsistent, that matters to real users.

    Beyond raw file size, there is the HTTP request count. Each static font file is a separate request. A variable font is one request. Fewer round trips, simpler caching strategy, less complexity in your <link rel="preload"> logic. It all compounds.

    How to Implement Variable Fonts in CSS

    Implementation is genuinely straightforward. If you are self-hosting a variable font (recommended for performance over relying on a third-party CDN), your @font-face declaration looks like this:

    @font-face {
      font-family: 'Inter';
      src: url('/fonts/Inter-Variable.woff2') format('woff2 supports variations'),
           url('/fonts/Inter-Variable.woff2') format('woff2');
      font-weight: 100 900;
      font-style: normal;
      font-display: swap;
    }

    The font-weight: 100 900 range declaration is what tells the browser this file covers the full weight axis. Once declared, you can use any value in that range in your CSS without downloading anything extra:

    h1 {
      font-weight: 750;
    }
    
    .caption {
      font-weight: 380;
    }

    That is not a typo. 750 and 380 are valid values. You are no longer constrained to multiples of 100. This is the design flexibility part that typographically-minded developers tend to get quite excited about.

    Developer coding variable fonts web design implementation with CSS font-variation-settings on screen
    Developer coding variable fonts web design implementation with CSS font-variation-settings on screen

    Using Font Variation Settings for Custom Axes

    Standard axes like wght and wdth map to familiar CSS properties. But custom axes require the lower-level font-variation-settings property. Four-letter axis tags in uppercase are custom; lowercase are registered standard axes. Here is an example using a hypothetical font with a custom CASL (casual) axis, which Recursive actually ships:

    body {
      font-variation-settings: 'wght' 400, 'CASL' 0.5;
    }
    
    .pull-quote {
      font-variation-settings: 'wght' 600, 'CASL' 1;
    }

    One gotcha worth knowing: font-variation-settings does not inherit individual values elegantly. If you set it on a parent and override it on a child, you need to re-declare all axes on the child or the unspecified ones snap to their defaults. It is one of those CSS specifics that bites everyone at least once. The workaround is to use CSS custom properties as axis value holders and reference them inside font-variation-settings:

    :root {
      --font-weight: 400;
      --font-casual: 0;
    }
    
    body {
      font-variation-settings: 'wght' var(--font-weight), 'CASL' var(--font-casual);
    }
    
    .pull-quote {
      --font-weight: 600;
      --font-casual: 1;
    }

    Now the child only overrides the custom property it needs to change, and the rest inherit correctly. Tidy.

    Animating Variable Font Axes with CSS

    Because variable font axes are numerical values, they are animatable. You can transition font-variation-settings in CSS, which opens up some genuinely striking UI effects without a single line of JavaScript. A weight transition on hover, for instance:

    .nav-link {
      font-variation-settings: 'wght' 400;
      transition: font-variation-settings 200ms ease;
    }
    
    .nav-link:hover {
      font-variation-settings: 'wght' 700;
    }

    That said, animating font axes is GPU-hungry when done carelessly. Stick to will-change: font-variation-settings on elements you know will animate, and avoid triggering reflows on large blocks of body text. Test on a mid-range Android handset, not just your MacBook Pro, because the render cost can be quite visible on lower-powered hardware.

    Where to Find Good Variable Fonts

    Google Fonts now has a solid and growing variable font catalogue, and all fonts are free to self-host. Fontshare, run by Indian Type Foundry, has some excellent variable options at no cost. For commercial projects with stricter brand requirements, type foundries like Dalton Maag (London-based, work with major UK brands) offer premium variable font licences that include the full axis range. It is worth checking licence terms carefully; some variable font licences restrict the number of axes you can use in web contexts, which is an odd quirk of the industry still working itself out.

    Variable Fonts and Optical Size: The Hidden Gem

    The opsz axis is the one most developers overlook entirely, and it is arguably the most typographically valuable. Optical size adjustments change the actual letterform design, not just scale, based on the intended size of use. A 12px caption and a 60px display heading are rendered at different weights and proportions automatically when font-optical-sizing: auto is set in CSS. This is how type was handled in quality print for centuries, and it has only been feasible on screen since variable fonts arrived. It is the kind of detail that makes a design feel expensive without being able to pinpoint exactly why.

    Is the Performance Argument Still Valid in 2026?

    Some engineers push back and argue that with HTTP/2 multiplexing and aggressive browser caching, the number-of-requests argument is less compelling than it used to be. Fair point. But the file size argument holds up even under that scrutiny. And the design flexibility argument has nothing to do with performance at all; it is purely about what you can build. Being able to set font-weight: 467 to hit exactly the visual weight your brand system specifies, without any extra asset, is just a better way to work. The variable fonts web design case was strong in 2020 when this landed; in 2026 it is essentially inarguable. There is no good reason to be shipping five static font files when one variable file does the same job better.

    If your current project is still on static fonts, running a quick audit with Chrome DevTools’ Network tab filtered to font resources will show you exactly what you are dealing with. The migration path is usually a morning’s work, and the performance uplift in Core Web Vitals, specifically the reduction in render-blocking time from font loading, tends to show up clearly in your Lighthouse scores within a few days of deployment.

    Frequently Asked Questions

    What is a variable font and how does it differ from a regular web font?

    A variable font is a single font file that contains an entire range of weights, widths, and other stylistic variations along continuous axes, rather than fixed snapshots. A regular static font file only contains one specific style, so you need multiple files to cover different weights. Variable fonts give you far more design flexibility with fewer files.

    Do variable fonts actually improve website loading speed?

    Yes, in most cases. A single variable font file is typically much smaller than the combined file size of equivalent static font files, and it requires only one HTTP request instead of several. This reduces page weight and simplifies caching, which can noticeably improve font loading performance, especially on slower mobile connections.

    Are variable fonts supported in all modern browsers?

    Browser support is essentially universal. Chrome, Firefox, Safari, and Edge have all supported the OpenType variable font specification for several years. You may encounter issues only with very old browser versions, but a straightforward fallback using the standard font stack handles those gracefully.

    Where can I find free variable fonts to use on my website?

    Google Fonts has a growing selection of free variable fonts that can be self-hosted. Fontshare by Indian Type Foundry also offers high-quality variable fonts at no cost. For commercial work requiring more distinct typography, foundries like Dalton Maag offer premium licensed variable fonts.

    Can I animate variable font axes in CSS without JavaScript?

    Yes. Because variable font axes are numerical values, CSS transitions and animations work on the font-variation-settings property directly. You can animate weight, width, or any custom axis purely in CSS. Be mindful of performance on lower-powered devices and use will-change sparingly on elements you know will animate.

  • No-Code vs Low-Code vs Full Code: Choosing the Right Build Approach for Your Next Project

    No-Code vs Low-Code vs Full Code: Choosing the Right Build Approach for Your Next Project

    The question of how to actually build something has never been more loaded. In 2026, the gap between dragging a block in Webflow and writing a custom API route in Next.js is enormous, but both approaches can ship a production-ready product. Understanding the no-code vs low-code vs full code 2026 landscape properly, rather than just defaulting to what you already know, is genuinely one of the most useful decisions you can make before a single pixel gets placed or a single line gets typed.

    This isn’t about which approach is objectively best. It’s about which one fits the project sitting in front of you right now. Let’s actually break it down.

    Designer and developer comparing no-code vs low-code vs full code 2026 build approaches at studio workstations
    Designer and developer comparing no-code vs low-code vs full code 2026 build approaches at studio workstations

    What Do We Even Mean by No-Code, Low-Code, and Full Code?

    These three terms get blurred constantly, usually by marketing teams trying to make their platform sound more accessible than it is. So let’s be precise.

    No-code means building entirely through a visual interface, no programming knowledge required. Webflow is the canonical example for websites. Framer sits in an interesting middle zone (more on that shortly). The idea is that logic, layout, and interactions are all abstracted behind GUI controls.

    Low-code means a visual-first environment that still expects you to write some code when complexity demands it. Framer’s React override system is a great example. You can build 90% of a site visually, then drop into TypeScript for a custom animation or data fetch. Platforms like Bubble fall here too for web apps.

    Full code means you’re writing everything from scratch, or close to it. Next.js, Remix, SvelteKit, raw React. You control the architecture, the performance, the data layer, all of it. The ceiling is unlimited. So is the time investment.

    The Case for No-Code: Webflow and the Visual Web

    Webflow has matured considerably. Its CMS is genuinely powerful for content-heavy marketing sites, and the Interactions panel gives motion designers a level of control that would have required GSAP and a developer two years ago. For a UK agency spinning up a client brochure site, a campaign landing page, or a portfolio, Webflow is hard to argue against on speed-to-launch alone.

    The honest limitations, though, are real. Custom authentication flows, complex database relationships, dynamic user dashboards — Webflow starts to creak. You’ll find yourself reaching for Memberstack, Airtable, Zapier, and a growing stack of third-party bolt-ons that each cost money and introduce failure points. At some point, you’re maintaining a Frankenstein architecture held together by webhooks and crossed fingers.

    Best for: marketing sites, portfolios, content-driven blogs, campaign pages, client projects where the brief is well-defined and scope is unlikely to balloon.

    Developer working on low-code build tools in a comparison of no-code vs low-code vs full code 2026
    Developer working on low-code build tools in a comparison of no-code vs low-code vs full code 2026

    The Case for Low-Code: Framer’s Interesting Proposition

    Framer occupies a genuinely interesting space in the no-code vs low-code vs full code 2026 conversation. It started as a prototyping tool, pivoted aggressively to being a publishing platform, and is now used by design-led teams at some serious companies. The visual canvas is arguably better than Webflow’s for highly expressive, animation-heavy sites. The component model maps closely enough to React that moving to full code later isn’t a complete rewrite.

    Where Framer shines is for design teams who want to own the build. Designers can ship real sites without waiting for a developer, but developers can drop into code overrides when something bespoke is needed. It’s collaborative in a way that feels natural rather than forced.

    The caveats: Framer’s CMS is still less mature than Webflow’s, and for anything approaching a real web application, you’ll outgrow it quickly. It’s also worth keeping an eye on pricing, as Framer’s plans have shifted a few times and costs can accumulate for larger teams. The UK government’s guidance on open standards in technology is a useful reminder that platform lock-in is a real risk worth evaluating before committing.

    Best for: design-led teams, portfolio sites with heavy animation, marketing pages for tech companies, projects where designer autonomy is a priority.

    The Case for Full Code: Next.js and Owning Everything

    Next.js remains the dominant React framework in 2026 for good reason. Server components, edge rendering, the App Router, built-in image optimisation, and a deployment pipeline that slots directly into Vercel or a self-hosted setup. If you’re building a SaaS product, an e-commerce platform, a membership site with real logic, or anything that needs to scale with user data, full code is the only honest answer.

    The trade-off is time and expertise. A Next.js project requires architectural decisions upfront: database choice, authentication strategy, state management, API design. You’re not dragging blocks; you’re writing components, managing dependencies, handling errors, writing tests. For a small studio or solo developer, the overhead is real.

    That said, the developer experience in 2026 is genuinely good. TypeScript tooling is excellent, component libraries like shadcn/ui have removed enormous amounts of boilerplate, and deployment is faster than ever. If you have the skills or the team, full code gives you nothing you can’t build.

    Best for: SaaS products, web applications with user accounts, e-commerce with custom logic, anything requiring a real backend, projects with long-term scale requirements.

    How to Actually Choose: A Practical Framework

    Here’s the decision tree I tend to use when scoping a new project.

    Start with the question: does this project need user accounts, custom logic, or a real database relationship beyond basic CMS fields? If yes, you’re in full code territory unless you want to spend months patching low-code workarounds.

    If no, ask: does the design require significant custom animation, unusual layout patterns, or component-level interactivity? If yes, Framer’s low-code model is worth serious consideration. If the design is relatively conventional, Webflow’s no-code environment will get you to launch faster.

    Timeline and budget matter enormously. A startup with two weeks and a tight budget to validate an idea should not be commissioning a bespoke Next.js application. A growing platform with paying users and a development team should not be running on Webflow CMS bolted to four third-party services.

    The Hybrid Reality Most Projects Actually Live In

    The honest truth about the no-code vs low-code vs full code 2026 decision is that most real-world projects are hybrid. A marketing site in Webflow or Framer pulling data from a headless CMS, connected to a Next.js backend that handles authentication and payments. Or a Framer front-end with code overrides calling a lightweight API. These architectures are increasingly common, and they work well when scoped deliberately.

    The mistake is letting the approach choose itself by default. Picking Webflow because you’ve always used Webflow, or defaulting to Next.js because it feels more serious. The tool should serve the project, not the other way around. Get that decision right upfront and everything downstream is easier.

    Frequently Asked Questions

    Is Webflow good enough for a real business website in 2026?

    Absolutely, for most marketing and content-driven sites. Webflow handles CMS, SEO, hosting, and interactions well enough for the vast majority of business websites. Where it struggles is with complex user logic, real databases, and application-level features.

    Can Framer replace a developer entirely?

    For design-heavy marketing sites and portfolios, Framer can often get a designer to a shipped product without a developer. However, anything requiring custom backend logic, authentication, or complex data handling will still need developer involvement, either through Framer’s code overrides or a separate API.

    When should I use Next.js instead of a no-code platform?

    Use Next.js when your project needs user accounts, complex data relationships, a custom API, or any logic that goes beyond what a CMS can handle. It’s also the better choice when performance at scale, long-term maintainability, or bespoke functionality are priorities.

    How much does it cost to build with Webflow vs Next.js?

    Webflow’s pricing starts from around £14 per month for basic sites, scaling up to £35+ for CMS and e-commerce plans. Next.js itself is open-source and free, but you’ll factor in hosting (Vercel’s free tier is generous, paid plans start around £16 per month) plus development time, which is significantly higher than no-code.

    What is the best build approach for a SaaS startup in 2026?

    Most SaaS products genuinely need full code, specifically a framework like Next.js, because user authentication, billing, dashboards, and data logic require real engineering. You might use a no-code tool for the marketing landing page, but the actual product needs a proper codebase.

  • How to Build a Design System From Scratch in 2026 Using Figma and Tokens Studio

    How to Build a Design System From Scratch in 2026 Using Figma and Tokens Studio

    Design systems have gone from being the exclusive obsession of large product teams at Monzo or the BBC to something every serious designer and developer needs to understand. And honestly? The barrier to entry has never been lower. If you want to build a design system in Figma 2026 that actually scales, that your devs will love, and that won’t collapse into chaos six months later, this guide is for you. We’re going step by step, and we’re bringing Tokens Studio along for the ride, because design tokens are where the real power lives.

    Designer working on a build design system Figma 2026 project at a modern London studio workstation
    Designer working on a build design system Figma 2026 project at a modern London studio workstation

    Why Design Tokens Are the Foundation You Can’t Skip

    Before you touch a single component, you need to sort your tokens. Think of tokens as variables for your design decisions. Colour values, spacing increments, font sizes, border radii, shadow depths — all of it lives in tokens rather than being hard-coded into individual components. The moment you hardcode #1A1A2E directly into 47 components, you’ve built a maintenance nightmare, not a system.

    Tokens Studio (the Figma plugin) lets you define, organise, and sync tokens in a JSON format that your developers can consume directly. It also integrates with Style Dictionary, which transforms those tokens into platform-specific outputs: CSS custom properties for the web, Swift constants for iOS, XML for Android. That’s the handoff pipeline sorted before a single component is drawn.

    Naming Your Tokens Properly

    Naming is where most teams go wrong. There are broadly three tiers of tokens, and understanding them saves enormous grief later.

    • Primitive (global) tokens: Raw values. color.blue.500 = #3B82F6. These are the periodic table of your system. Never reference them directly in components.
    • Semantic (alias) tokens: Meaningful assignments. color.action.primary = {color.blue.500}. This is what your components actually reference.
    • Component tokens: Scoped to a specific component. button.background.default = {color.action.primary}. Optional but powerful for complex components.

    The reason for this three-tier structure? When your brand pivots from blue to teal (and it will happen, trust me), you update one primitive token and the entire system propagates the change. Magic, but actually just good architecture.

    Setting Up Tokens Studio in Figma

    Install Tokens Studio from the Figma Community. Once it’s running, you’ll see a panel where you can create token sets. Start with a global set for your primitives and a semantic set that references them. In Tokens Studio, you reference another token using curly brace syntax: {color.blue.500}.

    For teams working collaboratively, connect Tokens Studio to a GitHub repository. Your token JSON lives in source control alongside your code. Designers push token changes via the plugin; developers pull them and run Style Dictionary to generate updated CSS variables. The design-to-code gap narrows dramatically. According to the UK Government Design Principles, consistency and clarity are foundational to good service design, and a token-based system is precisely how you enforce both at scale.

    Close-up of Figma and Tokens Studio JSON panel used to build design system in Figma 2026
    Close-up of Figma and Tokens Studio JSON panel used to build design system in Figma 2026

    Component Architecture: Building for Scale

    Now the fun part. Components in Figma should follow an Atomic Design hierarchy, even if you don’t call it that out loud in your team documentation.

    Atoms First

    Build your smallest, indivisible elements first. Buttons, input fields, checkboxes, radio buttons, badges, icons. Each atom should use semantic tokens exclusively, never raw values. Use Figma’s component properties to handle variants: state (default, hover, active, disabled), size (small, medium, large), and hierarchy (primary, secondary, ghost).

    Keep each atom in a dedicated page or dedicated section within a page. Organise your layers obsessively. Naming matters here: use the Group/Component Name slash convention so Figma’s asset panel presents them cleanly. Button/Primary/Default reads like a file path, which is exactly what it is.

    Molecules and Organisms

    Molecules combine atoms into slightly more complex chunks. A form field is a molecule: it combines a label atom, an input atom, and a helper text atom. An organism is a full section: a navigation bar, a hero block, a card grid. Each layer of composition should reference its children as nested component instances, not as detached copies. Detaching components is the design system equivalent of deleting a git history.

    Using Figma Variables Alongside Tokens Studio

    Figma’s native Variables (introduced in 2023 and considerably more mature now in 2026) and Tokens Studio overlap in interesting ways. Figma Variables are great for live mode switching — light and dark themes, for instance — because they’re native to the runtime. Tokens Studio is better for complex multi-brand setups and for the developer export pipeline. In practice, many teams use both: Variables for the Figma-side interactive prototyping experience, Tokens Studio for the actual production handoff. It’s not either/or; it’s knowing which tool solves which problem.

    Developer Handoff That Doesn’t Make Developers Want to Quit

    When you build a design system in Figma 2026, the handoff process is where the theory meets reality. A few non-negotiable practices:

    • Token JSON in the repo: Developers should be able to run npm run build:tokens and get updated CSS custom properties automatically. No more screenshots of hex codes.
    • Component documentation: Use Figma’s built-in description fields to document intended behaviour, states, and usage restrictions. If a component shouldn’t be used without an icon, say so in the description.
    • Storybook integration: If your devs are working in React or Vue, push them towards maintaining a Storybook instance that mirrors the Figma component library. When the two drift apart, you have a system problem, not a communication problem.
    • Change logs: Every token or component update should be logged. A simple markdown file in the repo works fine. Designers tend to forget that a subtle colour tweak can break contrast ratios across an entire product.

    Keeping the System Alive (The Hard Bit)

    Building a design system is about 30% of the work. The other 70% is governance, iteration, and adoption. Appoint a system owner, even if it’s a rotating responsibility. Schedule quarterly audits of your token sets and component library. Create a clear contribution process so product teams can propose additions without every random one-off component ending up in the core library.

    Teams that genuinely succeed at this, whether they’re a ten-person startup in Sheffield or a hundred-person product org in London, share one trait: they treat the design system as a product in its own right. It has a roadmap, a backlog, and real users (the rest of the design and engineering team).

    The payoff is real. Faster prototyping, dramatically reduced QA cycles, consistent accessibility compliance, and designers who can spend their time solving actual UX problems rather than recreating the same button variant for the fourteenth time. That’s why so many teams are keen to build a design system in Figma 2026 rather than continuing to wing it with ad hoc component libraries.

    If you take one thing from all of this: start with your tokens. Get the naming right. Everything else builds on top of that foundation, and a solid foundation means the whole system scales without drama. Now go make something properly good.

    Frequently Asked Questions

    What is a design token and why does it matter for a design system?

    A design token is a named variable that stores a design decision, such as a colour value, spacing size, or font size. Rather than hardcoding raw values into components, you reference tokens, which means updating a single token automatically propagates changes across the entire system without manual edits.

    Do I need to pay for Tokens Studio to use it with Figma?

    Tokens Studio has a free tier that covers basic token management and is perfectly usable for small teams or solo projects. The Pro tier unlocks advanced features like GitHub, GitLab, and Azure DevOps sync, multi-file token sets, and themes, which are essential for larger product teams managing multiple brands or complex theming.

    What is the difference between Figma Variables and Tokens Studio?

    Figma Variables are a native Figma feature suited to live prototype switching, such as toggling between light and dark mode directly in the canvas. Tokens Studio is a plugin that excels at complex token structures, multi-brand setups, and generating developer-ready outputs via Style Dictionary. Many teams use both together rather than choosing one over the other.

    How long does it take to build a design system from scratch in Figma?

    A basic but functional token-based design system with core atoms and a documented handoff pipeline can take one to three weeks for an experienced designer working full-time. A comprehensive system covering typography, spacing, colour, elevation, motion, and a full component library for a mature product can take two to four months. The governance and adoption phase never really ends.

    How do I make sure developers actually use the design system?

    The single biggest driver of developer adoption is reducing friction in the handoff process. When tokens are available as auto-generated CSS custom properties or platform-specific constants directly from the repo, developers don’t need to manually transcribe values, which removes a major pain point. Maintaining a Storybook that mirrors the Figma library also helps bridge the gap between design intent and coded implementation.

  • Micro-Interactions That Convert: The Psychology Behind Small UI Details

    Micro-Interactions That Convert: The Psychology Behind Small UI Details

    There is a moment in product design that nobody talks about enough. A user hovers over a button. The button pulses slightly, shifts colour by 10%, maybe adds a tiny shadow. The user clicks. They did not consciously notice any of that. But somewhere in their nervous system, something said “this thing is alive and trustworthy.” That is the quiet power of micro-interactions that convert, and if you are not thinking carefully about them, you are leaving real engagement on the table.

    This is not fluff. Research from Nielsen Norman Group consistently shows that feedback loops, even imperceptible ones, reduce cognitive friction and build perceived reliability. When an interface responds to you instantly and honestly, it feels competent. When it is static and silent, it feels unfinished. The gap between those two feelings? That is where conversion rates live.

    Designer studying micro-interactions that convert on a large monitor in a modern studio
    Designer studying micro-interactions that convert on a large monitor in a modern studio

    What Actually Makes a Micro-Interaction Work?

    The term gets thrown around loosely, so let us be precise. A micro-interaction is a single-purpose response to a user action. Dan Saffer, who literally wrote the book on this, breaks them into four components: trigger, rules, feedback, and loops and modes. That framework is still the most useful one I have come across.

    The trigger is what starts the interaction (a hover, a click, a form submission). The rules define what happens next. The feedback is the visible, audible, or tactile response the user gets. And loops govern whether that behaviour repeats or changes over time. Most developers get the trigger and feedback right, then stop. The loops and rules, the parts that adapt the interaction based on context, are where the genuinely clever stuff happens.

    Take something as mundane as a form validation message. A red border appearing after a failed submission is basic feedback. But an input field that gently shakes when you try to submit an empty required field? That is a rule-driven, physics-informed response. The user does not need to read an error message. Their brain has already processed “no” through the visual metaphor of resistance. Fewer words, faster correction, lower drop-off rate.

    Hover States: More Than Just Pointer Feedback

    Hover states are the most underestimated tool in the front-end designer’s kit. They are often treated as an afterthought, a quick :hover { background: #eee; } thrown in during QA. That is a waste of a genuinely persuasive moment.

    A well-designed hover state communicates affordance. It tells the user “yes, this is clickable, and here is roughly what clicking it will do.” Shopify stores consistently report higher add-to-basket rates when product cards have animated hover states that preview secondary product images or surface a quick-action button. That is not coincidence. That is the affordance principle at work: reveal the next action before the user commits to it, and the commitment feels less risky.

    In CSS, building this efficiently looks like this:

    .product-card {
      position: relative;
      overflow: hidden;
      transition: transform 0.2s ease, box-shadow 0.2s ease;
    }
    
    .product-card:hover {
      transform: translateY(-4px);
      box-shadow: 0 12px 24px rgba(0, 0, 0, 0.12);
    }
    
    .product-card .quick-action {
      opacity: 0;
      transform: translateY(8px);
      transition: opacity 0.2s ease, transform 0.2s ease;
    }
    
    .product-card:hover .quick-action {
      opacity: 1;
      transform: translateY(0);
    }

    Keep transitions between 150ms and 300ms. Under 100ms feels glitchy. Over 400ms feels sluggish. That 150-300ms window is where animations feel responsive without being jarring. You can cite this back to research from Google’s Material Design team if anyone questions you in a meeting.

    Hover state micro-interaction on a mobile product card demonstrating micro-interactions that convert
    Hover state micro-interaction on a mobile product card demonstrating micro-interactions that convert

    Feedback Loops and the Dopamine Hook

    Here is where it gets properly nerdy. Feedback loops in UI borrow heavily from behavioural psychology, specifically the concept of variable reward schedules. When a user takes an action and receives immediate, satisfying feedback, dopamine is released. The interface feels good to use. The user is more likely to take the next action.

    Think about the LinkedIn “like” animation. The little burst of colour and scale when you tap a reaction button is not decorative. It is a micro-reward. Instagram, Duolingo, and yes, even some of the better e-commerce apps in the UK (ASOS’s wishlist animation is a textbook example) have baked this thinking into their core interaction patterns.

    For button click feedback, the simplest high-impact technique is the “press” state. Not just :active with a colour change, but a physical downward shift combined with reduced shadow, simulating the button actually being pressed into the screen:

    .btn-primary:active {
      transform: translateY(2px);
      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
      transition: transform 0.05s ease, box-shadow 0.05s ease;
    }

    Fifty milliseconds. That is all you need. But it changes everything about how the button feels.

    Loading states are another feedback loop that developers habitually neglect. If a form submission takes two seconds and the UI does nothing in response, the user will click the button again. Now you have a double submission. A skeleton loader or a spinner with a subtle pulse animation solves this, reduces server load, and makes the wait feel shorter. It is a classic case where good UX and good engineering are the same decision.

    Building Micro-Interactions Efficiently at Scale

    The trap most teams fall into is building animations one-off, in component CSS, without any shared token system. You end up with 14 different easing curves across a codebase, and the whole product feels inconsistent in ways users cannot articulate but absolutely feel.

    The fix is to define a motion system early, exactly like a colour system or a type scale. In your design tokens, define a small set of approved durations and easing functions:

    :root {
      --duration-fast: 150ms;
      --duration-base: 250ms;
      --duration-slow: 400ms;
      --ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
      --ease-decelerate: cubic-bezier(0, 0, 0.2, 1);
      --ease-accelerate: cubic-bezier(0.4, 0, 1, 1);
    }

    Then every animation in your system references these tokens. When a designer decides the base duration feels slightly slow, you change one line. Job done. The BBC’s GEL design system handles motion exactly this way, and it shows: their cross-product experience feels coherent even across very different interfaces.

    For React projects, I tend to reach for Framer Motion for anything complex, and plain CSS transitions for simple hover and focus states. Do not use Framer Motion for a button hover. That is using a sledgehammer on a drawing pin. Keep the abstraction level matched to the complexity of the problem.

    Accessibility and Performance: The Non-Negotiables

    Micro-interactions that convert are not micro-interactions that make half your users feel ill. Vestibular disorders affect a meaningful portion of the population, and excessive or fast motion can trigger genuine discomfort. The prefers-reduced-motion media query is not optional:

    @media (prefers-reduced-motion: reduce) {
      *, *::before, *::after {
        animation-duration: 0.01ms !important;
        transition-duration: 0.01ms !important;
      }
    }

    Stick that in your base CSS and never think about it again. Your animations will still exist for users who want them; users who do not will get instant state changes. The WCAG 2.1 guideline 2.3.3 makes this a legal consideration for public sector UK digital services, and it is good practice everywhere else.

    On performance: always animate transform and opacity. These are composited by the browser’s GPU and do not trigger layout recalculation. Never animate width, height, top, left, or margin if you can help it. Those trigger reflow, and on lower-end Android devices common across the UK market, you will see dropped frames that make your polished interaction look broken.

    One final, slightly unexpected thought on micro-interactions: they are not exclusive to software. The same principles of reward, feedback, and delight that make a UI feel satisfying appear in all kinds of interactive experiences, physical and digital. If you want a genuinely charming example of feedback loops applied to a real product experience, have a look at how the folks behind a LEGO Subscription box service think about the unboxing ritual as a sequence of progressive reveals. The design thinking is transferable, even if the medium is cardboard rather than CSS.

    The bottom line is this: micro-interactions that convert are not decoration. They are information architecture expressed in motion. Every hover state is a prompt. Every loading animation is a promise. Every button press is a handshake. Design them deliberately, build them efficiently, and respect the users on the receiving end. That combination is what separates interfaces that just work from interfaces people genuinely enjoy using.

    Frequently Asked Questions

    What are micro-interactions in UI design?

    Micro-interactions are small, single-purpose animations or feedback responses triggered by a user action, such as a button changing colour on hover or a form field shaking when submitted incorrectly. They communicate system status, confirm actions, and guide users through an interface without requiring them to read lengthy instructions.

    Do micro-interactions actually improve conversion rates?

    Yes, when implemented thoughtfully. Micro-interactions reduce cognitive friction by making interfaces feel responsive and trustworthy, which lowers hesitation before clicking or submitting. Studies from Nielsen Norman Group and real-world data from e-commerce platforms consistently show improved engagement metrics when feedback states are well-designed.

    How do I build micro-interactions without hurting page performance?

    Stick to animating only CSS properties that the browser can composite on the GPU: primarily `transform` and `opacity`. Avoid animating layout-triggering properties like `width`, `height`, or `margin`, as these cause expensive reflow calculations. Keep transition durations between 150ms and 300ms for best perceived responsiveness.

    What tools or libraries are best for creating micro-interactions in 2026?

    For React projects, Framer Motion is the go-to for complex sequenced animations, while CSS custom properties and transitions handle simpler hover and focus states perfectly well on their own. For vanilla JS or Vue projects, the Web Animations API is increasingly capable and requires no additional dependencies.

    How do I make micro-interactions accessible for users with motion sensitivity?

    Use the `prefers-reduced-motion` CSS media query to detect users who have enabled reduced motion in their operating system settings, then strip back or disable animations accordingly. WCAG 2.1 guideline 2.3.3 specifically addresses animation from interactions, and compliance is mandatory for UK public sector digital services.

  • Variable Fonts and the Future of Web Typography: A Developer’s Deep Dive

    Variable Fonts and the Future of Web Typography: A Developer’s Deep Dive

    Typography on the web has been quietly having its best decade ever. And sitting right at the centre of that glow-up is something that, once you understand it, genuinely makes you annoyed that we spent so long without it. Variable fonts are one of those things that sounds like a minor technical curiosity until you actually pull back the curtain and realise the entire approach to loading, rendering, and animating type has fundamentally shifted. If you care about variable fonts web development, buckle in.

    The short version: a variable font is a single font file that contains the entire design space of a typeface. Weight, width, slant, optical size, and any number of custom axes the type designer has chosen to include. All baked into one file. One request. One render. Compare that to the old way of doing things, where you’d load a separate file for regular, bold, italic, bold italic, condensed, and so on. A website with four or five font variants could easily rack up half a megabyte of font requests before a single pixel of content loaded. Variable fonts fix that in an almost embarrassingly elegant way.

    Developer adjusting variable fonts web development axis in browser DevTools on a studio monitor
    Developer adjusting variable fonts web development axis in browser DevTools on a studio monitor

    How Variable Fonts Actually Work Under the Hood

    The OpenType specification (version 1.8, introduced back in 2016) added what’s called the OpenType Font Variations standard. Inside a variable font file, you have two kinds of data working together. First, you have the default glyph outlines, the master design, usually somewhere in the middle of the design space. Second, you have delta values: instructions that describe how each point in every glyph should move as you travel along a given axis.

    Think of it like a 3D mesh being deformed by invisible handles. The font engine interpolates between these delta states in real time, at render time, on the fly. It’s genuinely clever. A weight axis with a range of 100 to 900 doesn’t store nine separate sets of glyphs. It stores the default outlines plus the instructions for how points shift as weight increases or decreases. The maths is relatively lightweight, and modern rendering engines handle it with barely a blink.

    There are five registered axes in the OpenType spec that you’ll encounter most often: wght (weight), wdth (width), ital (italic), slnt (slant), and opsz (optical size). Beyond those, type designers can register entirely custom axes with four-character tags. The Recursive font, for example, has a MONO axis that morphs the typeface from a proportional sans-serif into a monospaced coding font. That’s not a gimmick. That’s genuinely useful for a codebase where you want visual consistency across UI and code samples.

    Implementing Variable Fonts with CSS: The Practical Bit

    Loading a variable font in CSS is mostly familiar territory, with one important addition. Your @font-face block needs to declare the ranges your font supports, otherwise the browser won’t know it’s variable and may not hand control of the axes over to you.

    @font-face {
      font-family: 'InterVariable';
      src: url('/fonts/inter-variable.woff2') format('woff2 supports variations'),
           url('/fonts/inter-variable.woff2') format('woff2');
      font-weight: 100 900;
      font-style: normal;
      font-display: swap;
    }
    

    That font-weight: 100 900 range is the key signal. It tells the browser this file covers the full weight spectrum. Once loaded, you can do things that would have required multiple files before. Setting font-weight: 350 on a heading, for instance. That’s not a value traditional font stacks could honour. With a variable font, it just works.

    The low-level axis control lives in font-variation-settings. Registered axes use lowercase tags; custom axes use uppercase.

    h1 {
      font-variation-settings: 'wght' 720, 'wdth' 85;
    }
    
    .mono-code {
      font-variation-settings: 'MONO' 1, 'wght' 400;
    }
    

    One thing worth knowing: font-variation-settings doesn’t inherit gracefully the way font-weight does. If you set it on a parent and want to override just one axis on a child, you have to re-declare all the axes. It’s a small gotcha that catches people out. The workaround is to use CSS custom properties as intermediaries, which makes the whole thing considerably cleaner to manage at scale.

    CSS code for variable fonts web development displayed on a laptop screen beside a mechanical keyboard
    CSS code for variable fonts web development displayed on a laptop screen beside a mechanical keyboard

    Animating Variable Fonts with CSS and JavaScript

    Here’s where it genuinely gets fun. Variable font axes are animatable. You can transition weight, width, slant, all of it, with CSS transitions or JavaScript. The results can be subtle and refined or completely unhinged, depending on what you’re after.

    A simple CSS transition on hover:

    .nav-link {
      font-variation-settings: 'wght' 400;
      transition: font-variation-settings 0.2s ease;
    }
    
    .nav-link:hover {
      font-variation-settings: 'wght' 700;
    }
    

    That’s a weight transition on hover, no JavaScript, no extra files. Smooth, performant, a single font request.

    For more dynamic control, JavaScript lets you tie axis values to user input, scroll position, cursor movement, or any other runtime data. A classic demo approach is mapping the mouse Y position to font weight across a heading. In practice, you’d reach for this kind of thing for interactive editorial pieces or portfolio hero sections where you want something genuinely memorable. The browser interpolates every frame, and because the glyph geometry is computed by the font engine rather than the GPU compositing layer, it stays crisp at any size.

    const heading = document.querySelector('h1');
    
    document.addEventListener('mousemove', (e) => {
      const weight = Math.round(100 + (e.clientY / window.innerHeight) * 800);
      heading.style.fontVariationSettings = `'wght' ${weight}`;
    });
    

    Straightforward, effective, and the kind of thing that lands differently in a live demo than it does in a paragraph of text.

    Why Variable Fonts Make Sense for Performance-Conscious Projects

    The performance case is fairly open and shut. Web font loading has historically been one of the trickier things to optimise for Core Web Vitals, particularly Largest Contentful Paint and Cumulative Layout Shift. Multiple font file requests introduce latency and render-blocking risk. A single variable font file, compressed in woff2 format, typically comes in smaller than three or four traditional weight files combined, even though it covers the entire range.

    According to the web.dev documentation maintained by Google’s Chrome team, a variable font can reduce font payload significantly compared to loading multiple static weights, with real-world savings depending heavily on the typeface and how many weights a project actually needs.

    Browser support is no longer a genuine concern in 2026. All modern browsers have supported variable fonts since well before this decade. The only edge case you’d realistically encounter is very old embedded browser environments, and font-display: swap handles graceful degradation there anyway. There’s very little reason not to use them.

    Variable Fonts Worth Actually Using in Projects

    For variable fonts web development work in 2026, a handful of typefaces come up repeatedly because they’re excellent and genuinely free. Inter Variable is practically standard at this point for UI work. Recursive is worth pulling in whenever you need a single typeface to span both interface copy and code samples. Fraunces is a beautiful display font with an optical size axis that makes it genuinely usable across wildly different contexts. All three are available via Google Fonts or direct download, with full variable font support.

    If you want to inspect what axes a font exposes before committing to it, the Wakamai Fondue tool (fondue.underscoretype.com) is the most useful browser-based font inspector I’ve found. Drop a font file in and it tells you everything: axes, ranges, named instances, OpenType features. Proper nerd-level font archaeology.

    The Bigger Picture for Design-Forward Web Work

    Variable fonts aren’t a trend. They’re an infrastructure upgrade. The way the web loads and renders type has genuinely improved, and the design space that opens up when you’re not constrained to a handful of preset weights is larger than it first appears. Fluid typography systems, where font weight and size scale continuously with viewport width rather than jumping at breakpoints, are much more viable when you’re not loading a separate file for each step. Responsive type becomes genuinely responsive, not just responsive-ish.

    The investment to get started is low. Swap one of your static font stacks for a variable equivalent on your next project. Spend twenty minutes with font-variation-settings. See what axis values your chosen typeface supports. The gap between knowing about variable fonts and actually using them confidently is smaller than most people expect, and the results speak loudly.

    Frequently Asked Questions

    What is a variable font and how is it different from a regular web font?

    A variable font is a single font file that contains an entire range of styles along one or more design axes, such as weight, width, or slant. A traditional web font file contains only one fixed style, so loading multiple weights means multiple file requests. A variable font handles all of those in a single download.

    Do variable fonts actually improve website performance?

    Yes, in most real-world cases. A single variable font file in woff2 format is typically smaller than loading three or four separate static weight files combined. Fewer HTTP requests and a smaller total payload generally translate to faster load times and better Core Web Vitals scores, particularly for Largest Contentful Paint.

    How do I use font-variation-settings in CSS?

    You declare it as a CSS property with axis tags and values, for example: font-variation-settings: ‘wght’ 600, ‘wdth’ 90. Registered axes like weight use lowercase four-character tags, while custom axes defined by the type designer use uppercase tags. Be aware that this property doesn’t inherit partially, so changing one axis on a child element requires re-declaring all axes.

    Can you animate variable fonts with JavaScript or CSS transitions?

    Yes, and it works extremely well. The font-variation-settings property is animatable via CSS transitions and animations, or you can update it dynamically with JavaScript tied to scroll position, mouse movement, or any other runtime event. The browser interpolates between axis values smoothly at render time, keeping the output crisp at any size.

    Which variable fonts are best for web projects in 2026?

    Inter Variable is the go-to for most UI work given its readability and comprehensive axis support. Recursive is excellent when you need a single typeface to cover both interface text and monospaced code samples. For display headings, Fraunces offers an optical size axis that adapts elegantly across very different sizes. All three are freely available with full variable font support.

  • Micro-Interactions in UX Design: Small Details That Make a Big Difference

    Micro-Interactions in UX Design: Small Details That Make a Big Difference

    There’s a moment when you tap the heart icon on a post and it bounces, pulses, and fills with colour. Takes maybe 300 milliseconds. You barely notice it consciously. But you felt something. That’s the magic of micro-interactions in UX design, and it’s the kind of detail that separates a product people love from one they merely tolerate.

    Micro-interactions are the tiny, single-purpose animations and feedback loops baked into interfaces. A toggle that slides smoothly. A loading bar that fills with personality. A form field that subtly shakes when you enter the wrong password. None of these are the main feature. All of them are load-bearing walls in the structure of user experience.

    Designer working on micro-interactions in UX design on a laptop in a modern studio
    Designer working on micro-interactions in UX design on a laptop in a modern studio

    Why Micro-Interactions in UX Design Actually Matter

    It’s tempting to dismiss them as decoration. They’re not. They do several measurable jobs simultaneously. They communicate system status (something happened, the thing worked), they guide behaviour (you did it right, or wrong), and they add emotional texture to an otherwise flat digital surface.

    Research cited by the Nielsen Norman Group has long established that response times under 100ms feel instantaneous to users, while delays up to one second break the flow of thought. Micro-interactions live in that critical window. When an animation confirms an action within 200ms, the user’s brain registers cause and effect as seamless. When there’s nothing? Uncertainty creeps in. Did it work? Should I tap again?

    Dan Saffer’s framework for micro-interactions, which has basically become the canonical reference, breaks each one into four components: a trigger, rules, feedback, and loops/modes. The trigger starts it (a hover, a click, a system condition). The rules define what happens. The feedback is what the user sees, hears, or feels. Loops determine whether it repeats. Simple framework. Wildly useful when you actually apply it.

    Animation Timing: The Difference Between Slick and Sluggish

    Timing is where most developers get micro-interactions wrong. Either too fast to register, or so slow they feel like the interface is wading through treacle. Getting this right is genuinely nerdy and I love it.

    The general sweet spot for most UI feedback animations is between 150ms and 400ms. Shorter than that and human perception misses the feedback entirely. Longer than 500ms and it starts feeling like lag rather than intentional motion. For transitions that carry meaning (like a modal sliding in), 300ms with an ease-out curve tends to feel natural because it mirrors the physics of objects decelerating to rest.

    In CSS, transition and animation properties give you this control directly. A basic hover micro-interaction might look like this:

    .btn {
      transform: scale(1);
      transition: transform 200ms ease-out, box-shadow 200ms ease-out;
    }
    
    .btn:hover {
      transform: scale(1.05);
      box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
    }

    That two-property transition costs essentially nothing in performance and immediately makes the button feel alive. Crucially, transform and opacity are the two CSS properties you should reach for first because they’re composited by the browser and don’t trigger layout recalculation. Animating width, height, or margin? That’s asking for jank.

    Smartphone screen showing a micro-interaction toggle animation in a mobile UX design
    Smartphone screen showing a micro-interaction toggle animation in a mobile UX design

    Designing Micro-Interactions with Purpose: Core Principles

    Not every pixel needs to dance. One of the more common mistakes I see in interface reviews is what I’d call micro-interaction soup: every element animated, every action celebrated, until the whole thing feels like a toddler’s birthday party rather than a professional product. Restraint is a skill.

    A few principles that hold up well in practice:

    Match energy to context

    A banking app confirming a payment transfer should feel calm and reassuring, not exuberant. A creative tool or game can afford something more expressive. The animation should feel like it belongs to the brand and context, not like it was borrowed from a dribbble shot.

    Use easing functions that feel physical

    Linear motion looks robotic. CSS gives you ease, ease-in, ease-out, ease-in-out, and full cubic-bezier() control. For most UI interactions, ease-out (starts fast, decelerates) feels most natural because objects in the real world decelerate. Reserve ease-in (starts slow, accelerates) for elements exiting the screen.

    Never hide information behind animation

    The feedback loop must be clear. If a form submission fails, the micro-interaction should make that failure unmistakably obvious, not bury it in a pretty wiggle. Accessibility also matters here: users who have enabled prefers-reduced-motion in their operating system should get functional feedback without the motion. Always wrap animations in a media query check:

    @media (prefers-reduced-motion: no-preference) {
      .icon {
        transition: transform 250ms ease-out;
      }
    }

    JavaScript-Powered Micro-Interactions: When CSS Isn’t Enough

    CSS handles most standard micro-interactions cleanly, but some scenarios need JavaScript’s logic. Think: a like button that counts up, a progress indicator that responds to real upload speed, or a drag-and-drop interface that gives haptic-style visual feedback based on position.

    The Web Animations API (WAAPI) is worth knowing if you’re doing anything complex. It gives you JavaScript control over animations with the performance benefits of CSS-level compositing. A simple implementation:

    const button = document.querySelector('.like-btn');
    
    button.addEventListener('click', () => {
      button.animate([
        { transform: 'scale(1)' },
        { transform: 'scale(1.3)' },
        { transform: 'scale(1)' }
      ], {
        duration: 300,
        easing: 'ease-out'
      });
    });

    For heavier animations involving scroll-triggered sequences or complex state transitions, libraries like GSAP remain the go-to in professional production environments. Framer Motion is the choice for React-based projects, with its whileHover and whileTap props making micro-interaction implementation almost embarrassingly simple.

    Speaking of things that affect focus and performance: it’s a bit like comparing the precision engineering behind oxygen tanks to a garden hose. The underlying mechanism matters enormously, even when the visible output looks similar.

    Real-World Implementation: Where to Start

    If you’re auditing an existing product or starting a new one, prioritise micro-interactions at the highest-frequency touchpoints first. Buttons, form inputs, toggles, and loading states are touched hundreds of times per session. These are where well-crafted feedback compounds most rapidly into a perception of quality.

    A practical starting checklist:

    • Every interactive element should have a visible hover and active state.
    • Form validation feedback should be immediate and clearly directional (green border for valid, red with a shake for invalid).
    • Loading states should show progress, not just a spinner. A determinate progress bar is always more reassuring than indeterminate spinning.
    • Success and error confirmations should use both colour and motion, never just colour alone (colour-blind users exist, and your UI should work for them).
    • All animations should respect prefers-reduced-motion.

    The cumulative effect of getting these right is difficult to quantify precisely, but qualitative user research consistently shows that interfaces with cohesive micro-interactions are perceived as faster, more trustworthy, and more premium than technically equivalent interfaces without them. Users can’t always articulate why. They just feel the difference.

    The Bottom Line

    Micro-interactions in UX design are one of the highest-return-on-investment details you can invest time in. They don’t require huge engineering effort, they don’t require a visual overhaul, and they don’t require a design system rebuild. They require careful thought about timing, feedback, and purpose, applied consistently across the touchpoints that matter most. Get them right and your product starts to feel like it was made by people who genuinely cared about the experience. Which, presumably, you are.

    Frequently Asked Questions

    What are micro-interactions in UX design?

    Micro-interactions are small, single-purpose animations or feedback mechanisms built into a user interface. They confirm actions, communicate system status, and add emotional texture to digital products. Examples include a button scaling slightly on hover, a toggle sliding smoothly, or a form field shaking when an incorrect value is entered.

    How long should micro-interaction animations be?

    Most UI micro-interaction animations should fall between 150ms and 400ms. Shorter than that and users won’t register the feedback; longer than 500ms and it starts to feel like lag. For transitions carrying meaning, such as modals or drawers, 300ms with an ease-out curve tends to feel most natural.

    Which CSS properties are best for micro-interactions?

    Use transform and opacity wherever possible because they are composited by the browser and don’t trigger layout recalculation, resulting in smooth, jank-free animations. Avoid animating properties like width, height, or margin, as these cause expensive layout reflows.

    Do micro-interactions affect accessibility?

    Yes, and this is important. Some users have vestibular disorders or motion sensitivity and enable the prefers-reduced-motion setting in their OS. Always wrap non-essential animations in a @media (prefers-reduced-motion: no-preference) query so those users receive functional feedback without the motion. Never rely on colour alone for feedback either.

    Should I use CSS or JavaScript for micro-interactions?

    CSS handles the majority of standard micro-interactions efficiently and is the right first choice. Use JavaScript (via the Web Animations API or libraries like GSAP or Framer Motion) when you need logic-driven animations, such as interactions that respond to dynamic data, scroll position, or complex state changes that CSS cannot manage on its own.

  • CSS in 2026: The New Features That Are Changing How We Write Stylesheets

    CSS in 2026: The New Features That Are Changing How We Write Stylesheets

    CSS has had a proper glow-up. For years it felt like the language was stuck in a loop of hacks, overrides, and seventeen nested divs just to centre something vertically. But the last couple of years have genuinely changed the game, and the new CSS features 2026 developers are now reaching for daily are the kind of thing that would have required a JavaScript library two years ago. No joke. We’re talking about layout tools, animation systems, and cascade management that actually make sense as design primitives.

    This isn’t a “look what’s in the spec” post. These features have broad browser support right now, and if you’re not using them yet, you’re probably writing more code than you need to. Let’s get into it.

    Developer working with new CSS features 2026 on a dual-monitor setup in a home office
    Developer working with new CSS features 2026 on a dual-monitor setup in a home office

    Container Queries: Finally, Truly Responsive Components

    For a long time, responsive design meant responding to the viewport. Media queries fired based on how wide the browser window was, which is fine until you’re building a component library and you have absolutely no idea what container your card is going to land in. A sidebar card versus a full-width card shouldn’t need two entirely different stylesheets, but that’s what we were stuck with.

    Container queries fix this properly. You define a containment context on a parent element, and then child elements can query that parent’s size rather than the viewport. Here’s a stripped-down example:

    .card-wrapper {
      container-type: inline-size;
      container-name: card;
    }
    
    @container card (min-width: 400px) {
      .card {
        display: grid;
        grid-template-columns: 1fr 2fr;
      }
    }
    

    That’s it. The card now responds to its own container, not the screen. Pair this with something like a design system built in Figma, and your components start mapping much more cleanly to real-world layout behaviour. I’ve personally stopped writing duplicate breakpoint logic for sidebar vs main content columns entirely. Container queries have made that problem obsolete.

    Cascade Layers: Sanity for Large CSS Codebases

    Specificity wars are the CSS equivalent of merge conflicts you caused yourself. Cascade layers, introduced via the @layer rule, give you explicit control over the order in which sets of styles are applied, completely independent of selector specificity. This is quietly one of the most powerful things to land in CSS in years.

    The idea is simple: you declare your layers upfront, then assign styles to them. Styles in a later layer win over earlier ones, regardless of specificity.

    @layer reset, base, components, utilities;
    
    @layer reset {
      * { margin: 0; padding: 0; box-sizing: border-box; }
    }
    
    @layer components {
      .button {
        background: #3b82f6;
        color: white;
        padding: 0.5rem 1rem;
        border-radius: 0.25rem;
      }
    }
    
    @layer utilities {
      .mt-4 { margin-top: 1rem; }
    }
    

    A utility class in the utilities layer will always override a component style in components, even if the component selector is technically more specific. No more !important bodge jobs. No more specificity calculator tabs. This is the kind of structure that makes CSS actually scalable across a team, and it integrates beautifully with methodologies like ITCSS if you’re into that sort of thing.

    Close-up of a keyboard and CSS code notes illustrating new CSS features 2026 development workflow
    Close-up of a keyboard and CSS code notes illustrating new CSS features 2026 development workflow

    Scroll-Driven Animations: JavaScript Who?

    Scroll-triggered animations used to mean pulling in Intersection Observer, writing event listeners, managing animation states, and hoping IntersectionObserver fired at the right threshold on every browser. Now? You can do a surprisingly large chunk of that work entirely in CSS.

    The new CSS features 2026 scroll animation spec introduces two new timeline types: scroll() and view(). The scroll() timeline ties an animation’s progress to a scrollable container’s scroll position. The view() timeline ties it to an element’s position within the viewport.

    @keyframes fade-in-up {
      from {
        opacity: 0;
        transform: translateY(24px);
      }
      to {
        opacity: 1;
        transform: translateY(0);
      }
    }
    
    .reveal {
      animation: fade-in-up linear both;
      animation-timeline: view();
      animation-range: entry 10% cover 40%;
    }
    

    That block produces a fade-in-upward entrance animation tied entirely to the element’s scroll position into the viewport. No JS. No dependency. According to MDN Web Docs, browser support for scroll-driven animations via animation-timeline has expanded significantly, with Chromium-based browsers leading the charge and Firefox catching up. For production use, a @supports check is still wise, but for progressive enhancement this is genuinely usable today.

    :has() Selector: The Parent Selector We Always Needed

    For the longest time, CSS could only look downward through the DOM. You could style children based on their parent, but never the reverse. The :has() pseudo-class changes that, and developers who’ve clocked this are using it everywhere.

    /* Style a form group differently when its input has focus */
    .form-group:has(input:focus) {
      outline: 2px solid #3b82f6;
      border-radius: 4px;
    }
    
    /* Style a card differently when it contains an image */
    .card:has(img) {
      padding: 0;
    }
    

    The second example alone replaces what used to require a JavaScript class toggle. You can now style containers based on what’s inside them, which opens up component logic that was previously only achievable through scripting. Combined with container queries, :has() is part of a double act that fundamentally shifts how much CSS can do on its own.

    CSS Nesting: Native at Last

    If you’ve used Sass or Less at any point in the last decade, native CSS nesting will feel like coming home. Browsers now support nested selectors without a preprocessor in the loop at all.

    .nav {
      display: flex;
      gap: 1rem;
    
      & a {
        color: inherit;
        text-decoration: none;
    
        &:hover {
          text-decoration: underline;
        }
      }
    }
    

    Is this a game-changer on the level of container queries? Not quite. But it does meaningfully reduce the cognitive overhead of writing and reading CSS. Fewer repetitions of parent class names, better visual grouping of related rules, and one fewer reason to compile Sass for basic projects. For small-to-medium projects especially, you can now drop the preprocessor entirely and lose nothing.

    What This All Means for How You Structure CSS in 2026

    The interesting thing about the new CSS features 2026 landscape is that they aren’t isolated tricks. They form a coherent system. Cascade layers give you a sensible architecture. Container queries give your components layout awareness. Scroll-driven animations remove JS overhead from interaction design. :has() reduces state management. Native nesting reduces verbosity. Stack them together and you’re writing less code, with fewer dependencies, and far fewer specificity headaches than even three years ago.

    Front-end development is genuinely more fun right now than it’s been in a while, and CSS is a big reason why. If your current workflow still leans heavily on jQuery scroll plugins, BEM-as-a-specificity-hack, or a Sass compiler purely for nesting, it might be time for a proper audit of what you actually still need.

    The language has grown up. Worth growing with it.

    Frequently Asked Questions

    Are the new CSS features in 2026 safe to use in production?

    Most of them, yes. Container queries, cascade layers, CSS nesting, and the :has() selector all have strong cross-browser support in Chromium, Firefox, and Safari. Scroll-driven animations are solid in Chromium-based browsers but still have some gaps in Firefox, so a @supports fallback is recommended for those.

    What is the difference between container queries and media queries?

    Media queries respond to the viewport size, meaning the entire browser window. Container queries respond to the size of a parent element instead, which makes components truly portable regardless of where they’re placed in a layout. This is especially useful in component-based design systems.

    Do I still need Sass or Less now that CSS has native nesting?

    For simple-to-medium projects, probably not for nesting alone. Native CSS nesting handles the core use case. However, Sass still offers features like functions, mixins, and more powerful loops that CSS custom properties don’t fully replicate yet, so the answer depends on your project’s complexity.

    How do cascade layers work with third-party CSS libraries like Bootstrap?

    You can wrap third-party styles inside a named @layer to deliberately lower their priority, which makes overriding them much simpler without resorting to !important. This is one of the most practical use cases for cascade layers in real-world projects.

    Can scroll-driven animations replace JavaScript libraries like GSAP?

    For simple scroll-triggered entrance effects, yes, native scroll-driven animations can replace basic GSAP or Intersection Observer setups entirely. For complex timeline sequencing, physics-based animations, or fine-grained control, GSAP still offers capabilities that CSS alone can’t match.

  • The Ultimate Beginner’s Guide to Learning React in 2026

    The Ultimate Beginner’s Guide to Learning React in 2026

    React is still the dominant force in front-end development, and if you want to learn React in 2026, you’ve picked a genuinely good time to start. The ecosystem has matured enormously. The chaotic, “twelve different ways to do state management” energy of a few years ago has settled into something far more coherent. There are clearer paths, better tooling, and a community that has collectively agreed on quite a lot of things that used to cause endless arguments on Stack Overflow at midnight.

    That said, beginners still run into the same walls. They watch a tutorial, build a counter app, feel great, then open a real codebase and feel completely lost. This guide is about bridging that gap: what to actually learn, in what order, and why the stuff most tutorials skip is usually the stuff that matters most.

    Developer at desk coding to learn React 2026 with component tree visible on screen
    Developer at desk coding to learn React 2026 with component tree visible on screen

    The Modern React Ecosystem: What You Actually Need in 2026

    First, a quick orientation. React itself is a UI library, not a full framework. It handles the view layer. Everything else, routing, data fetching, server rendering, is handled by tools built around it. In 2026, the two ecosystems worth your attention are Next.js and Remix. If you’re aiming for a job at a UK startup or agency, Next.js is almost certainly what you’ll encounter. It’s the safe bet. Remix is brilliant and teaches you web fundamentals in a way Next.js sometimes obscures, but Next.js has the bigger job market.

    For state management, the landscape has simplified. React’s built-in hooks (useState, useReducer, useContext) handle a huge amount of what used to require Redux. When you do need something more powerful, Zustand is lightweight and sensible. TanStack Query (formerly React Query) is the go-to for server state, and honestly, once you understand the difference between server state and client state, a massive chunk of React complexity suddenly makes sense.

    Styling in 2026? Tailwind CSS has won the utility-class argument for most teams. You’ll encounter it constantly. Learn it. CSS Modules are still solid for more traditional approaches, and CSS-in-JS solutions like Emotion still exist in older codebases, but Tailwind is the pragmatic choice for anyone starting fresh.

    Where to Actually Learn React: Resources Worth Your Time

    The React documentation at react.dev is genuinely excellent now. It was rewritten a couple of years back with hooks as the default, and it includes interactive sandboxes throughout. Start there. Seriously, the official docs are not boring placeholder content; they’re a proper learning path written by people who understand how beginners think.

    Beyond the docs, a few resources stand out. Scrimba has interactive React courses that let you code directly in the browser as you watch. The Odin Project is a free, open-source curriculum with a strong UK community following, and it covers React in proper context alongside HTML, CSS, and JavaScript fundamentals. For video content, Jack Herrington on YouTube is technically rigorous without being dry. He covers advanced patterns without making you feel like you need a computer science degree to follow along.

    One thing I’d strongly recommend: do not jump into React before you are genuinely comfortable with modern JavaScript. Destructuring, spread operators, array methods like map and filter, async/await, and ES modules. React makes heavy use of all of these. If JavaScript concepts are fuzzy, React will feel like magic in the worst sense, and you’ll be copying code you don’t understand.

    Close-up of React code with TypeScript on a laptop screen while learning React 2026 concepts
    Close-up of React code with TypeScript on a laptop screen while learning React 2026 concepts

    Key Concepts Beginners Constantly Overlook

    Here’s where I see people get stuck. Not on the basics, but on the concepts that tutorials gloss over because they’re harder to demonstrate in a ten-minute YouTube video.

    The Component Mental Model

    React is built around components, and understanding what makes a good component is more art than science at first. The principle of single responsibility applies here: a component should ideally do one thing. When components become enormous with ten different concerns tangled together, they become almost impossible to maintain. Practise breaking UI into small, composable pieces from the start.

    useEffect Is Not a Lifecycle Method

    This trips up almost everyone who learned React before hooks, and it confuses beginners who read older tutorials. useEffect is for synchronising your component with an external system. It is not a direct replacement for componentDidMount. The dependency array is not optional decoration. Getting useEffect wrong is one of the most common sources of bugs in React applications, full stop.

    Understanding Re-renders

    React re-renders a component when its state or props change. Simple enough. But when you have components passing data down several levels, or when you have large lists rendering unnecessarily, performance suffers. Understanding when React re-renders, and tools like React DevTools Profiler to actually measure it, is what separates someone who can build React apps from someone who can build React apps that perform well.

    TypeScript Is Not Optional Anymore

    If you’re learning React in 2026 and you’re ignoring TypeScript, you are actively making your future self miserable. The overwhelming majority of production React codebases at UK companies use TypeScript. It adds a small amount of upfront friction and pays back tenfold in catching errors, improving editor autocomplete, and making code self-documenting. Learn it alongside React, not after.

    Project Ideas That Actually Build Real Skills

    Tutorial projects are fine for learning syntax. But you need to build things that have real complexity. Here’s a progression that works well.

    Start with a GitHub user search app using the GitHub REST API. It introduces you to data fetching, loading states, error handling, and conditional rendering. All four of those will appear in every real project you ever build. Then move to a personal finance tracker with local storage persistence. This forces you to think about state management properly and how data flows between components. Once you’re comfortable, try building a full-stack app with Next.js using a backend like Supabase or PlanetScale. At this point you’re building something genuinely close to what companies actually ship.

    The BBC’s Bitesize digital skills resources point out that building real projects is the most effective way to consolidate technical learning, and that applies directly here. Reading and watching only gets you so far. You have to break things and fix them yourself.

    The Job Market Reality for React Developers in the UK

    React skills are consistently among the most requested in UK front-end job listings. According to data from the ONS and various industry surveys, the UK tech sector added tens of thousands of software roles in 2025, and front-end and full-stack React positions form a disproportionate share of junior and mid-level vacancies. London, Manchester, Bristol, and Edinburgh all have healthy React job markets. Remote roles are abundant too, which is a significant shift from five years ago.

    Employers in 2026 are not just looking for React knowledge in isolation. They want to see TypeScript, some familiarity with testing (Jest and React Testing Library are the standard), Git fluency, and ideally some experience with a meta-framework like Next.js. If your portfolio shows you can build, deploy, and explain a reasonably complex application, you’re in a far stronger position than someone who’s completed twenty courses but hasn’t shipped anything.

    A Realistic Timeline for Learning React Properly

    People underestimate how long this takes, and that causes unnecessary discouragement. If you’re starting from a solid JavaScript base and putting in consistent effort, three to four months to build something deployable and interview-ready is a realistic target. Not three to four months of passive watching, but active building. That means writing code daily, debugging real errors, and reading documentation rather than always reaching for a tutorial.

    The React ecosystem rewards patience. There’s a lot to absorb, but the learning curve has a clear shape. Fundamentals click, then patterns click, then performance and architecture click. Give each stage the time it needs rather than rushing to the next shiny concept. The developers who learn React properly the first time rarely need to relearn it from scratch six months later. The ones who skip the foundations absolutely do.

    Frequently Asked Questions

    Do I need to know JavaScript before I learn React in 2026?

    Yes, and this is non-negotiable. You should be comfortable with modern JavaScript concepts including ES6 syntax, array methods like map and filter, destructuring, async/await, and modules before starting React. Jumping in without this foundation means you’ll be confused about what’s JavaScript and what’s React, which makes debugging almost impossible.

    Is React still worth learning in 2026, or has something replaced it?

    React is absolutely still worth learning. It remains the most widely used front-end library in UK job listings and the broader industry. Frameworks like Next.js and Remix, both built on React, have expanded its relevance into full-stack development. Alternatives like Vue and Svelte exist and are excellent, but React’s ecosystem and job market are unmatched.

    How long does it take to learn React well enough to get a job?

    For someone with solid JavaScript foundations studying consistently, three to four months of active, project-based learning is a realistic timeline to reach job-ready level. This assumes you’re building real projects, not just watching tutorials. Adding TypeScript and Next.js to your portfolio significantly improves your chances with UK employers.

    Should I learn Next.js at the same time as React?

    It’s better to spend at least a few weeks on React itself before layering in Next.js. Understanding how React works without the framework means you’ll understand what Next.js is actually doing for you, rather than treating it as a black box. Once you’re confident with components, hooks, and basic state management, transitioning to Next.js is relatively straightforward.

    What is the best free resource to learn React in 2026?

    The official React documentation at react.dev is the single best free resource, featuring interactive examples and a structured learning path built around modern hooks-based React. The Odin Project is another excellent free option that contextualises React within a broader full-stack curriculum, and it has an active UK community for support.

  • Dark Mode Design Done Right: Best Practices for Apps and Websites

    Dark Mode Design Done Right: Best Practices for Apps and Websites

    Dark mode has graduated from a novelty toggle to a genuine design discipline. Almost every major platform — iOS, Android, Windows 11, macOS — ships with it built in, and users expect it. But here is the thing: slapping a dark background behind your existing UI is not dark mode design. It is dark mode chaos. Getting it right means understanding how light, colour, and contrast behave differently when the canvas flips, and there are specific dark mode design best practices that separate the polished from the painful.

    Designer working on dark mode design best practices at a studio desk with a dark UI displayed on monitor
    Designer working on dark mode design best practices at a studio desk with a dark UI displayed on monitor

    Why Dark Mode Is Harder Than It Looks

    The assumption most developers make is that dark mode is roughly the inverse of light mode. Swap white for near-black, swap dark text for light text, ship it. Except human vision does not work like a colour picker. On a light interface, our pupils constrict slightly. On a dark interface, they dilate. That shift changes how we perceive colour saturation, edge definition, and fine typographic detail. Colours that look perfectly saturated on a white background can appear garish and vibrating on dark grey. Typography that reads crisply at regular weight suddenly looks too thin. These are not aesthetic opinions; they are physiological realities.

    Then there is the accessibility dimension. The Web Content Accessibility Guidelines (WCAG) specify minimum contrast ratios: 4.5:1 for body text and 3:1 for large text and UI components. These ratios apply just as strictly in dark mode, and in practice they are easier to violate. A light grey on a slightly lighter dark background can feel visually distinct to someone with perfect vision while failing the contrast check entirely for someone with a visual impairment. Tools like the Paciello Group’s Colour Contrast Analyser (free, Windows and macOS) are genuinely useful here and worth keeping in your toolkit.

    Choosing the Right Background Colours

    Pure black (#000000) is almost never the right call. It creates an extreme luminance contrast that causes visual halation, a phenomenon where bright text on absolute black appears to bleed or glow at the edges. This is particularly noticeable on OLED displays, which are everywhere now. Google Material Design recommends #121212 as a baseline dark surface. Apple’s Human Interface Guidelines use a layered system of near-blacks and dark greys to create depth.

    The practical approach is to build a surface scale: a base colour around 8-12% lightness in HSL, then stepped layers at 14-16%, 18-20%, and so on. Each layer represents an elevation in your UI. Cards sit slightly lighter than the base. Modals sit slightly lighter than cards. This creates a sense of hierarchy without using shadows (which work poorly on dark backgrounds). The difference between each step should be subtle, roughly 5-8% lightness, enough to perceive without creating jarring jumps.

    Close-up of phone and laptop showing dark mode design best practices with layered surface colours
    Close-up of phone and laptop showing dark mode design best practices with layered surface colours

    Dark Mode Colour Theory: Desaturate Everything

    This is the rule most people skip, and it is why their dark UIs look like a rave flyer. Brand colours and accent colours that look great in light mode are almost always too saturated for dark mode. A vivid blue at full saturation on a light background reads as confident and clear. On a dark background, it screams. The fix is to desaturate and slightly lighten your accent colours for dark mode. If your brand blue is hsl(220, 80%, 50%) in light mode, try something closer to hsl(220, 50%, 65%) for dark mode. You get the same hue identity with far less visual noise.

    The same logic applies to status colours. Error reds, success greens, and warning yellows all need adjustment. Fully saturated red on near-black is practically unusable for anything beyond a brief flash. Bring the saturation down, bring the lightness up, and you get something that communicates urgency without being physically uncomfortable to look at.

    Typography in Dark Mode: Weight and Spacing Matter

    Text rendering on dark backgrounds is genuinely tricky. On light backgrounds, letterforms are defined by dark ink on a bright field. On dark backgrounds, the relationship inverts, and thin strokes in a typeface can disappear or appear broken, especially on non-retina displays. The practical implications are straightforward: avoid thin or extra-light font weights for body text in dark mode. Regular and medium weights hold up far better. If you are using a variable font, consider bumping the weight axis by 50-100 units specifically for your dark mode stylesheet.

    Letter spacing is worth revisiting too. Slightly increased tracking, around 0.01em to 0.02em on body text, can improve legibility on dark backgrounds. It is a small change but it compounds across paragraphs. Equally, do not go to pure white (#FFFFFF) for your body text. It is too bright against dark greys and contributes to eye fatigue over long reading sessions. A slightly off-white like #E8E8E8 or #F0F0F0 brings down the harshness while maintaining a perfectly comfortable contrast ratio.

    Handling Images, Illustrations, and Icons

    Images with white or near-white backgrounds become glaring rectangles in dark mode. The options are: use transparent PNGs or SVGs wherever possible, apply a subtle border-radius and a low-opacity border to separate image edges from the background, or use CSS filters like brightness(0.9) as a mild global dimming on images in dark mode. None of these are perfect solutions for every case, but using transparent assets by default is the cleanest approach and saves you problem-solving later.

    Icons deserve specific attention. Outlined icon sets often look visually heavier in dark mode because the stroke appears brighter than expected. Test your icon set in both modes early in the design process. Sometimes switching from outline to filled icons for dark mode, or offering both as a toggle, is the right answer depending on your UI density.

    Common Mistakes Developers and Designers Still Make

    Beyond the theoretical, there are specific implementation errors I see repeatedly in code review and design critiques. First: hardcoding colours instead of using a token or custom property system. If your dark mode colours are scattered across a stylesheet rather than defined as CSS custom properties and toggled at the :root level via a [data-theme="dark"] attribute or the prefers-color-scheme media query, you will spend weeks untangling it when the design changes.

    Second: ignoring the system preference altogether. The prefers-color-scheme media query has excellent browser support now, above 96% globally according to Can I Use. Not hooking into it means users with system-level dark mode enabled get a blinding light UI before they can find your manual toggle. Respect the preference by default; let users override it if they want.

    Third: forgetting focus states. Keyboard navigation focus rings that look fine in light mode can become almost invisible in dark mode if they rely on dark outlines. Make sure your :focus-visible styles use a colour with sufficient contrast in both modes. This is both an accessibility requirement and just decent design.

    Testing Dark Mode Properly

    Browser DevTools now include dark mode simulation. In Chrome, open DevTools, go to the Rendering tab, and set the emulated CSS media feature to prefers-color-scheme: dark. Firefox has the same capability. Use this constantly. Also test on actual devices: a phone screen at medium brightness in a dimly lit room is the canonical dark mode use case, and it reveals issues that a calibrated monitor in a bright studio will hide.

    Getting dark mode right is not about following a single rule; it is about understanding that every design decision has a different weight when light and dark swap roles. Spend the time on your colour tokens, check your contrast ratios religiously, and treat dark mode as a first-class design target rather than a theme you bolt on at the end. Your users, especially those using your app at midnight with one eye open, will genuinely thank you for it.

    Frequently Asked Questions

    What contrast ratio do I need for dark mode to pass accessibility standards?

    WCAG 2.1 requires a minimum contrast ratio of 4.5:1 for normal body text and 3:1 for large text (18pt or 14pt bold) and UI components. These requirements apply equally in dark mode, and are just as easy to fail if you use mid-toned greys carelessly. Always verify with a contrast checker tool rather than relying on your eyes alone.

    Should I use pure black as the background in dark mode?

    Generally no. Pure black (#000000) creates extreme luminance contrast that causes a halation or glow effect around bright text, particularly on OLED screens. Most design systems, including Google Material Design, recommend a very dark grey around #121212 as the base surface colour, which is far more comfortable for extended viewing.

    How do I handle brand colours that look too bright in dark mode?

    Desaturate and slightly lighten your brand accent colours specifically for dark mode. A fully saturated colour at 80% saturation on a dark background can appear aggressive and hard to look at. Reducing saturation to around 50% and increasing lightness by 10-15% preserves the hue identity while making the colour feel comfortable in a darker environment.

    What is the best way to implement dark mode in CSS?

    Define all your colours as CSS custom properties on the :root element, then override them inside a [data-theme=’dark’] attribute selector or a prefers-color-scheme: dark media query. This approach centralises all your colour tokens, makes switching seamless, and keeps your stylesheets maintainable as the design evolves.

    Do I need separate typography settings for dark mode?

    It is worth considering, yes. Thin and extra-light font weights can lose definition on dark backgrounds, so bumping to regular or medium weight for dark mode body text improves legibility. Slightly increasing letter spacing by around 0.01-0.02em and using an off-white like #E8E8E8 rather than pure white also reduces eye fatigue during extended reading.