Free vector avatars and icons in UI design: SVG libraries, licences and code

Free vector avatars and icons in UI design: SVG libraries, licences and code

Last verified: October 12, 2026
26 min read
Guide
Full-stack developer
Marketing strategist

User interface design has changed a lot over the past two decades. Expensive icon licences and pixel-by-pixel asset exports are gone. Modern web projects rely on scalable, lightweight SVG (Scalable Vector Graphics) libraries, programmatic avatar engines, and vector animations. For developers and UI designers this means high-performance, responsive interfaces built from free, open-source resources. This guide covers how icons moved from bitmaps to SVG, the leading free icon and avatar libraries with their licences, SVG security in WordPress, build-time icon pipelines, animation, and accessibility.


#1. Why are raster PNG icons outdated

The history of digital icons follows the technical limits of each era. In the early web, designers worked with bitmap graphics: images made of fixed pixels that lost quality when scaled beyond their original size. Icons were exported as PNG or GIF files at fixed dimensions, typically 16x16, 24x24, 32x32, and 64x64 pixels, and every size needed its own version.

Icon design in the 1990s and early 2000s was about pixel-level precision. At 16x16 pixels every pixel carried visual weight, and the “pixel art” look came from necessity rather than taste: designers smoothed edges by hand to keep shapes readable at low resolutions. In 2008 Smashing Magazine published a “Vector Girl Avatars” collection distributed as PSD and AI files, a good snapshot of the Web 2.0 aesthetic of gradients, shadows, and detailed illustrations. Back then every webmaster knew the routine of downloading a ZIP of icons and cropping them in Photoshop before publishing.

#What were the limits of PNG and GIF icons

  • Pixelation: Rescaling an image to a custom intermediate size forced the browser to interpolate pixels, resulting in blurry, unreadable icons.
  • Retina scaling issues: High-DPI (Retina) screens required separate @2x and @3x assets, adding design work and download weight.
  • Request overhead: Each icon was an independent HTTP request, so developers built CSS sprites (several images combined into one file and positioned with negative background offsets).
  • Transparency: PNG alpha transparency was a real step forward, because icons could sit on any background without the jagged edges of the one-bit transparency in GIF. The core limit stayed: scaling past the designed size still produced visible pixelation, while screens of different pixel densities kept multiplying.

Legacy libraries like Tango, Silk Icons by FamFamFam, and Fugue Icons were critical milestones, but their raster-based architecture could not scale to support modern responsive layouts.


#2. SVG vs PNG icons in modern web design

Scalable Vector Graphics (SVG) solved the bitmap problem because it describes an image independently of resolution. A bitmap defines an image as a grid of coloured pixels. SVG describes shapes, lines, curves, fills, and strokes mathematically, so they render sharply at any scale.

#How SVG became the standard for icons

SVG 1.1 is a W3C Recommendation. Its successor, SVG 2, still has Candidate Recommendation status: W3C published the latest snapshot of the specification on 6 October 2026. In practice, adoption was driven by native browser support and by the ability to place an <svg> element directly in HTML5. Responsive web design, which spread from around 2010, sped up the change further, because designers needed graphics that stay sharp on screens of any size and pixel density.

#SVG icon performance and accessibility benefits

  • Infinite scalability: The same icon renders correctly on a smartwatch and on a 5K monitor. One source file replaces a set of resolution-specific versions, which cuts both team effort and the number of files to download.
  • Small file size: The Heroicons magnifying-glass icon (24/outline/magnifying-glass.svg in the project repository) is 296 bytes, and the Lucide search.svg icon is 275 bytes (measured on 12 October 2026). SVG is a text format (XML), so the server can compress it in transit just like HTML and CSS, and the browser renders it without plugins.
  • Control from code: CSS and JavaScript can change any element of an inline SVG, which enables hover effects, animation, and styling that depends on application state.
  • Accessibility: SVG supports <title> and <desc> elements that assistive technology can read, and ARIA attributes mark an icon as meaningful or decorative (details in section 9). Vector graphics also cope better with page zoom: when a user enlarges text, an SVG icon scales without the artefacts that appear when a bitmap is enlarged beyond its native resolution. Because SVG properties are styleable, an icon can follow user preferences such as the prefers-color-scheme and prefers-contrast media queries.

#What are Lottie animations

The next step after static SVG was animated vector graphics. Lottie is an Airbnb library that, according to the lottie-web project documentation, parses Adobe After Effects animations exported as JSON with the Bodymovin plugin and renders them natively in web and mobile apps.

A Lottie file is a JSON representation of an After Effects animation. It holds what the renderer needs: paths, fills, transforms, and keyframe data. The library supports playback, looping, seeking to a given frame, and changing properties from code. For interface design this means loading animations, success confirmations, onboarding sequences, and micro-interactions can be vector-based and controlled by code instead of being embedded as GIF or video.

#SVG icons in a design system

Business applications and large sites need many icons: actions, status indicators, navigation elements, and concepts. Keeping hundreds of icons visually consistent is a challenge the vector approach handles systematically.

Design systems treat vector icons as base components. When basic shapes, stroke weight, corner radius, and optical corrections are parameters of the whole system, new icons stay consistent with existing ones. This limits the small inconsistencies that appear when each designer draws icons their own way. SVG icons also work well with design tokens: icon colour, size, and stroke weight can be managed alongside colours, spacing, and typography, so changing one central token definition updates icons across the whole application.


#3. SVG security in WordPress: a secure sanitization pipeline

WordPress does not include SVG in its list of allowed upload types by default. The reason is technical: a PNG image is pixel data, while SVG is an XML document that can carry nested elements like <script> tags, inline event handlers (e.g., onload), and references to external resources. Allowing untrusted users to upload SVGs to your WordPress media library introduces Cross-Site Scripting (XSS) and XML External Entity (XXE) risks. The restriction can be lifted safely if every uploaded file is sanitized and only trusted users may upload SVG.

#Understanding XSS in SVG documents

Cross-Site Scripting via SVG works because browsers render SVGs inside the DOM or as stand-alone documents. If an attacker embeds a script tag like <script>alert(document.cookie)</script> or an inline handler like <svg onload="javascript:alert(1)"> into the SVG file, and a user opens the media URL directly in the browser, the script executes in the context of your site’s domain. That script can read session data or perform actions on behalf of a logged-in administrator. Strict XML sanitization is therefore a hard requirement.

#How to prevent XXE and billion laughs attacks in SVG

Both XML attacks below depend on a document type declaration (<!DOCTYPE) that declares entities (<!ENTITY):

  1. XML External Entity (XXE) injection: An external entity can point at a local file (e.g., file:///etc/passwd). If the parser substitutes entities, it reads that file into the document, and the sanitized output carries its contents back to the attacker.
  2. XML entity expansion (billion laughs): Nested internal entities expand exponentially during parsing (lol1 expands to ten lol strings, lol2 to ten lol1 strings, and so on). This exhausts server memory and causes a denial of service.

A legitimate icon has no reason to declare entities, so the sanitizer below rejects any file containing <!DOCTYPE or <!ENTITY before it reaches the parser, and returns an upload error. The remaining files are loaded with LIBXML_NONET only, which blocks network access. The flags often copied into tutorials do the opposite of what they promise: LIBXML_NOENT enables entity substitution (php.net notes that its name is misleading, because it causes entities to be loaded and inserted), and LIBXML_DTDLOAD and LIBXML_DTDATTR load the DTD. libxml_disable_entity_loader() has been deprecated since PHP 8.0, because libxml 2.9.0 and later disable external entity loading by default.

#How to sanitize SVG uploads in PHP

Add this class to a site plugin to intercept and sanitize SVG uploads:

declare(strict_types=1);

namespace WPPoland\Security\Media;

class SVG_Sanitizer {
    public static function register(): void {
        add_filter( 'wp_handle_upload_prefilter', [ __CLASS__, 'sanitize_uploaded_svg' ] );
        add_filter( 'upload_mimes', [ __CLASS__, 'allow_svg_mime' ] );
    }

    public static function allow_svg_mime( array $mimes ): array {
        $mimes['svg'] = 'image/svg+xml';
        return $mimes;
    }

    public static function sanitize_uploaded_svg( array $file ): array {
        if ( 'image/svg+xml' !== $file['type'] ) {
            return $file;
        }

        $file_path = $file['tmp_name'];
        if ( ! file_exists( $file_path ) || ! is_readable( $file_path ) ) {
            return $file;
        }

        $svg_content = file_get_contents( $file_path );
        if ( false === $svg_content ) {
            return $file;
        }

        // Reject DTDs and entity declarations before parsing (XXE, billion laughs)
        if ( false !== stripos( $svg_content, '<!DOCTYPE' ) || false !== stripos( $svg_content, '<!ENTITY' ) ) {
            $file['error'] = 'SVG files with DOCTYPE or ENTITY declarations are not allowed.';
            return $file;
        }

        $dom = new \DOMDocument();
        libxml_use_internal_errors( true );

        // No entity substitution, no DTD loading, no network access
        if ( ! $dom->loadXML( $svg_content, LIBXML_NONET ) ) {
            $file['error'] = 'The SVG file could not be parsed.';
            return $file;
        }

        $xpath = new \DOMXPath( $dom );

        // Remove dangerous elements
        $blacklisted_tags = [ 'script', 'iframe', 'object', 'embed', 'foreignObject' ];
        foreach ( $blacklisted_tags as $tag ) {
            $elements = $xpath->query( sprintf( '//*[local-name()="%s"]', $tag ) );
            if ( $elements ) {
                foreach ( $elements as $element ) {
                    $element->parentNode->removeChild( $element );
                }
            }
        }

        // Clean attributes (inline event handlers and javascript: links)
        $elements = $xpath->query( '//@*' );
        if ( $elements ) {
            foreach ( $elements as $attribute ) {
                $name    = strtolower( $attribute->nodeName );
                $is_href = in_array( $name, [ 'href', 'xlink:href' ], true );
                if ( 0 === strpos( $name, 'on' ) || ( $is_href && 0 === strpos( strtolower( trim( $attribute->nodeValue ) ), 'javascript:' ) ) ) {
                    $attribute->ownerElement->removeAttribute( $attribute->nodeName );
                }
            }
        }

        file_put_contents( $file_path, $dom->saveXML() );

        return $file;
    }
}
SVG_Sanitizer::register();

This class shows the principle. For production, use the enshrined/svg-sanitize library by Daryll Doyle, the same sanitizer the Safe SVG plugin uses, because a maintained allowlist covers far more edge cases than a short blocklist.

#Safe SVG and SVG Support plugins for WordPress

Safe SVG is the simplest route. According to its WordPress.org plugin page, it is developed by 10up, has more than a million active installations, and sanitizes uploads with Daryll Doyle’s svg-sanitizer library (enshrined/svg-sanitize). It strips dangerous code on upload, shows SVG previews in the media library, can restrict SVG uploads to selected users, and can optionally optimize files with SVGO (off by default). Install and activate it, limit who may upload SVG if needed, and upload through the media library: sanitization runs automatically on every upload.

SVG Support by Benbodhi (plugin page) also sanitizes uploads and has an advanced mode: the style-svg class added to an image block renders the SVG inline, so you can style it with CSS.


#4. Best free open-source SVG icon libraries

The icon library market in 2026 includes classic sets as well as systems with many variants.

#Heroicons (Tailwind Labs)

Built by the creators of Tailwind CSS, Heroicons comes in four variants: 24x24 outline, 24x24 solid, 20x20 solid, and 16x16 solid. The @heroicons/react and @heroicons/vue packages expose the icons as components, and the project repository states the MIT licence. Heroicons fits Tailwind projects but works anywhere.

#Lucide Icons

Lucide is a community-maintained fork of Feather Icons with more than 1600 icons according to its repository. The icons share stroke weight, corner rounding, and grid structure. Lucide is licensed under ISC, while the icons that come from Feather remain under MIT. It has official packages for React, Vue, Svelte, Angular, Astro, and other environments.

#Feather Icons

Feather focuses on simplicity: every icon is drawn on a 24x24 grid with a default stroke width of 2. The project is MIT-licensed.

#Tabler Icons

According to its README, Tabler Icons includes 6238 icons (5184 outline and 1054 filled, as of October 2026), drawn on a 24x24 grid with a 2 px stroke and licensed under MIT. Outline icons can be made lighter or bolder by changing stroke-width.

#Phosphor Icons

Phosphor is an icon family in 6 weights: Thin, Light, Regular, Bold, Fill, and Duotone, licensed under MIT. Every icon exists in all weights, so sets combine without clashes. Phosphor has implementations for React, Vue, Figma, and web components.

#Material Symbols (Google)

Material Symbols is Google’s variable icon font with four axes: fill (0 to 100), weight (100 to 700), grade (-50 to 200), and optical size (20 to 48 px). According to Google’s repository, the icons are released under the Apache License 2.0. Each axis is set with CSS:

.material-icon-custom {
    font-variation-settings:
        'FILL' 1,
        'wght' 300,
        'GRAD' 0,
        'opsz' 24;
}

#Choosing between MIT, Apache 2.0, and SIL open font licenses

“Free” does not mean “without conditions”. The differences between licences are mostly about attribution and about what has to travel with copies of the files.

SetLicenceWhat it requires
Font Awesome Freeicons CC BY 4.0, fonts SIL OFL 1.1, code MITattribution; downloaded files already carry it in comments
Material SymbolsApache 2.0keep the licence notice when redistributing
HeroiconsMITinclude the copyright and licence notice with copies
LucideISC (icons derived from Feather: MIT)copyright and licence notice in copies
Phosphor, Tabler, FeatherMITcopyright and licence notice in copies
HumaaansCC0nothing
DiceBearcode MIT, styles under their own licencesdepends on the style, e.g. Fun Emoji CC BY 4.0

The Font Awesome Free licence file separates three licences: CC BY 4.0 for the icons, SIL OFL 1.1 for icons packaged as web and desktop fonts, and MIT for the remaining files. The same file asks you not to remove the attribution comments already present in the downloaded files.

MIT and ISC require the copyright notice and licence text to be included with copies of the software. The Lucide licence page does not mention a visible credit on the page; the obligation concerns the notice in distributed files. Apache 2.0 also includes an explicit patent grant. SIL Open Font License (OFL), commonly used for icon fonts, allows packaging icons as custom webfonts but restricts selling the font files by themselves.

#Social media, flag, and payment icons

Besides general-purpose libraries there are specialised sets.

Social media icons contain vector logos of the major platforms for share buttons, contact sections, and footers. A platform logo remains its owner’s trademark, so check that platform’s brand guidelines.

Flag icons cover countries and regions in vector form, useful for language switchers, shipping calculators, and country pickers. Sets usually name files by ISO 3166-1 country codes, which makes mapping easy.

Payment method icons contain logos of payment cards, digital wallets, and local payment methods, used in online shop checkouts.


#5. Free vector avatar generators and illustration libraries

#SVG avatar generators: DiceBear, Humaaans and others

Instead of picking avatars from a finite library, developers can generate them from a user ID, a name, or another attribute.

DiceBear generates SVG avatars from an input string (seed), so the same user always gets the same avatar. The simplest integration is a URL with the identifier as a parameter:

<img src="https://api.dicebear.com/10.x/lorelei/svg?seed=mariusz" alt="" width="64" height="64" />

According to the DiceBear HTTP API documentation, the current API version is 10.x (versions 5.x to 8.x are deprecated), the limit is 50 requests per second for SVG, and the public API is free for non-commercial use. For commercial use or higher limits, the documentation recommends running your own instance. The DiceBear code is MIT-licensed, but each avatar style has its own licence, listed on the DiceBear licences page: Lorelei is CC0 1.0, Fun Emoji is CC BY 4.0 (attribution required), and Avataaars and Bottts have their authors’ own terms, which allow personal and commercial use.

Humaaans by Pablo Stanley is a modular illustration system: a designer combines heads, torsos, legs, and backgrounds into scenes and characters. The project site states a CC0 licence, allowing commercial and personal use, and links to a Sketch version, an InVision Studio example, and a collection on Blush. The modular approach suits applications that need many different avatars: instead of choosing from fixed combinations, you can generate variants from components and match them to the brand.

Other generators worth knowing:

  • Boring Avatars: a React library (MIT) from the boringdesigners organisation with six variants (marble, beam, pixel, sunset, ring, bauhaus); the avatar is built from the name property, which can be a username or an email address.
  • Multiavatar: a generator whose authors list 12,230,590,464 possible avatars on the project site; the full licence terms are on a separate project page.
  • Bean Heads (formerly Big Heads): a React component by Robert Broersma, MIT-licensed, that assembles characters from properties such as hair, eyes, and skin colour.

#Free vector illustration libraries

Illustration libraries provide graphics designed to be used together, with a consistent style, palette, and visual language.

unDraw by Katerina Limpitsouni is a collection of flat-style illustrations covering topics from business to everyday activities. On the project site you set the accent colour before downloading, so the illustration matches the brand palette straight away. unDraw has its own licence, published on the project site, and it is worth reading before using it in a client project. In practice you pick an illustration, set the accent colour, download the SVG, and embed it in the template. The SVG can be styled further with CSS properties or inline attributes.


#6. How to inline SVG icons at build time in Astro

If you use a headless stack (e.g., Astro or Next.js) with WordPress as your back-end CMS, loading dynamic icon bundles at run-time can increase your client-side JavaScript bundle sizes. Instead, resolve and inline SVG icons during the static build phase.

Here is an example of an Astro component that reads an icon statically during compilation:

---
// Icon.astro - A build-time SVG inline fetcher
interface Props {
  name: string;
  className?: string;
}

const { name, className = 'icon-svg' } = Astro.props;

// Read SVG content from a local directory or fetch from API
const svgPath = `src/assets/icons/${name}.svg`;
let svgContent = '';

try {
  const fs = await import('fs/promises');
  const raw = await fs.readFile(svgPath, 'utf8');
  
  // Extract content inside the <svg> wrapper
  svgContent = raw.replace(/<svg[^>]*>|<\/svg>/g, '');
} catch (err) {
  console.error(`Build Error: Could not locate icon: ${name}.svg`);
}
---

<svg 
  xmlns="http://www.w3.org/2000/svg" 
  viewBox="0 0 24 24" 
  class={className} 
  fill="none" 
  stroke="currentColor" 
  stroke-width="2" 
  set:html={svgContent}
/>

This component generates zero client-side JavaScript, inlining the vector data directly into your HTML during the static compilation phase.

#How to build an SVG sprite sheet at build time

To avoid inlining the same SVG paths several times in one document, build a static sprite sheet. Each icon becomes a <symbol>, and the path code reaches the page once even when the icon appears many times:

// Vite/Astro build script snippet
import fs from 'fs/promises';
import path from 'path';

async function buildSvgSprite(iconDir, outputFile) {
  const files = await fs.readdir(iconDir);
  let spriteContent = '<svg xmlns="http://www.w3.org/2000/svg" style="display:none;">\n';

  for (const file of files) {
    if (path.extname(file) === '.svg') {
      const id = path.basename(file, '.svg');
      const content = await fs.readFile(path.join(iconDir, file), 'utf8');
      
      // Extract contents and wrap in <symbol>
      const cleanContent = content
        .replace(/<svg[^>]*>/, `<symbol id="icon-${id}" viewBox="0 0 24 24">`)
        .replace('</svg>', '</symbol>');
      
      spriteContent += cleanContent + '\n';
    }
  }
  
  spriteContent += '</svg>';
  await fs.writeFile(outputFile, spriteContent);
}

Output the generated sprite once per page (in a WordPress theme, the wp_body_open hook is a good place), then reference each icon with <use>: <svg class="icon" aria-hidden="true"><use href="#icon-search"></use></svg>.

#How to optimize SVG files with SVGO

Optimization matters most on pages with many icons. According to its documentation, SVGO is a Node.js library and command-line tool for optimizing SVG. It removes editor metadata, comments, hidden elements, and default or non-optimal values when that can be done without changing how the graphic looks. Run it in your theme or site build, before the sprite script above, so the sprite is assembled from already cleaned files.

Lazy loading applies only to SVG files inserted with <img>: loading="lazy" works for them like for any other image. An inline SVG is part of the HTML, so there is nothing to load lazily.


#7. How to generate SVG avatars as Base64 data URIs

To avoid storing thousands of individual profile images for comment sections or directories, you can generate vector avatars programmatically. Return these avatars as base64-encoded Data URIs, allowing you to use them directly in CSS background properties or standard src attributes.

Here is a PHP implementation that generates geometric SVG avatars and returns them as Data URIs:

declare(strict_types=1);

namespace WPPoland\UI\Avatars;

/**
 * Generates a base64 encoded SVG Data URI based on a string hash.
 */
function get_avatar_data_uri( string $seed ): string {
    $hash = md5( strtolower( trim( $seed ) ) );
    
    $color_primary   = '#' . substr( $hash, 0, 6 );
    $color_secondary = '#' . substr( $hash, 6, 6 );
    
    $shape_type = hexdec( substr( $hash, 12, 1 ) ) % 4;
    $rotation   = hexdec( substr( $hash, 13, 2 ) );

    $svg = sprintf(
        '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100" style="background:%s;">',
        esc_attr( $color_secondary )
    );

    $svg .= sprintf( '<g transform="rotate(%d 50 50)">', $rotation );

    switch ( $shape_type ) {
        case 0:
            $svg .= sprintf( '<circle cx="50" cy="50" r="30" fill="%s" />', esc_attr( $color_primary ) );
            break;
        case 1:
            $svg .= sprintf( '<polygon points="50,15 15,80 85,80" fill="%s" />', esc_attr( $color_primary ) );
            break;
        case 2:
            $svg .= sprintf( '<polygon points="50,10 90,50 50,90 10,50" fill="%s" />', esc_attr( $color_primary ) );
            break;
        case 3:
            $svg .= sprintf( '<rect x="25" y="25" width="50" height="50" rx="10" fill="%s" />', esc_attr( $color_primary ) );
            break;
    }

    $svg .= '</g></svg>';

    return 'data:image/svg+xml;base64,' . base64_encode( $svg );
}

This helper outputs a scalable avatar without storing any image file:

<img src="<?php echo esc_attr( get_avatar_data_uri( '[email protected]' ) ); ?>" alt="User Avatar" />

#8. How to animate SVG icons and interactive graphics

#How to animate SVG with CSS

CSS animations on SVG elements need no JavaScript. Two common interface patterns are a spinning loading indicator and a bouncing notification icon:

@keyframes iconSpin {
    from { transform: rotate(0deg); }
    to { transform: rotate(360deg); }
}

.icon.loading {
    animation: iconSpin 1s linear infinite;
}

@keyframes iconBounce {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(-5px); }
}

.icon.notification {
    animation: iconBounce 0.6s ease-in-out 2;
}

These rules work with inline SVG and can react to hover, application state, or classes added by a script.

#How to add a Lottie animation to a web page

Lottie animations are embedded through a player that handles rendering and playback control:

<!-- Lottie player pinned to a version (npm: @lottiefiles/lottie-player) -->
<script src="https://unpkg.com/@lottiefiles/[email protected]/dist/lottie-player.js"></script>

<!-- Animation served from your own server -->
<lottie-player
    src="/animations/loading.json"
    background="transparent"
    speed="1"
    style="width: 200px; height: 200px;"
    loop
    autoplay
></lottie-player>

The player version is pinned because @latest can change how the page behaves after a package update without any change in your code. Keep the animation JSON on your own server. In WordPress, Lottie animations can be embedded with a shortcode or a block, and the plugin directory has plugins that add a Lottie block with playback controls.

#How SVG animations affect performance

Limit the scope of animation to the elements that matter instead of the whole graphic. Animate specific properties such as opacity, transform, or fill, not properties that trigger layout recalculation.

Use transform for movement (translate, scale, rotate). Browsers can animate transform and opacity without recalculating page layout, which is not true of top, left, or margin.

Respect prefers-reduced-motion. Users who are sensitive to motion can turn on reduced motion in their operating system, and the page should then show a static or much simpler version:

@media (prefers-reduced-motion: reduce) {
    * {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }
}

#9. SVG and vector icon best practices

#How to keep a consistent icon style

A shared grid makes icons take up the same space and carry similar visual weight. Heroicons, Feather, Lucide, and Tabler draw icons on a 24x24 grid, and Material Symbols has a default optical size of 24 px. When you combine icons from different sources, a shared grid is the first condition for consistency.

A uniform stroke weight keeps line thickness the same across the set. Mixing thin and thick strokes in one interface looks chaotic. When a set offers several weights, pick one for the whole interface.

Optical corrections adjust shapes to human perception. A circle and a square of the same dimensions do not look equally large, so the circle usually extends slightly past the square’s edge. Well-crafted icon libraries have these corrections built in.

#SVG icon colours and dark mode

currentColor makes an icon inherit the text colour of its container. In the SVG file set fill="currentColor" or stroke="currentColor", and control the colour in CSS:

.icon {
    color: #666;
}

.icon:hover {
    color: #ff6b6b;
}

CSS custom properties keep icon colours organised across the system:

:root {
    --icon-primary: #333;
    --icon-secondary: #666;
    --icon-accent: #ff6b6b;
    --icon-disabled: #ccc;
}

@media (prefers-color-scheme: dark) {
    :root {
        --icon-primary: #eee;
        --icon-secondary: #aaa;
    }
}

.icon { color: var(--icon-primary); }
.icon.secondary { color: var(--icon-secondary); }
.icon.accent { color: var(--icon-accent); }
.icon.disabled { color: var(--icon-disabled); }

#SVG icon accessibility: title, aria-hidden and role=“img”

A screen reader cannot see an icon, so every icon must be either described or hidden. The choice depends on whether the icon carries information.

A decorative icon next to text that already says the same thing should leave the accessibility tree. MDN lists icons as a typical use of aria-hidden="true" and warns: “Do not use aria-hidden=“true” on focusable elements.” (MDN, aria-hidden). The attribute is inherited by children, so it must not go on the parent of a button or link.

A meaningful inline icon gets role="img" and an accessible name. MDN recommends role="img" on the outer <svg> so the screen reader treats the graphic as one unit instead of reading its child elements (MDN, img role). When a visible label exists, point to it with aria-labelledby; otherwise use aria-label.

The <title> element gives the graphic a short accessible description, which browsers usually show as a tooltip. For SVG 1.1 compatibility it should be the first child of its parent (MDN, title element).

An icon as <img> from an SVG file follows the same rules as other images. W3C WAI recommends an empty alt="" for decorative images, because without an alt attribute some screen readers announce the file name (W3C WAI, decorative images).

<!-- Icon-only button: name on the button, icon hidden -->
<button type="button" aria-label="Search">
    <svg aria-hidden="true" focusable="false" viewBox="0 0 24 24"><use href="#icon-search"></use></svg>
</button>

<!-- Meaningful icon without visible text -->
<svg role="img" aria-labelledby="status-title" viewBox="0 0 24 24">
    <title id="status-title">Status: active</title>
    <circle cx="12" cy="12" r="8" fill="currentColor"/>
</svg>

<!-- Decorative icon as a file -->
<img src="/icons/star.svg" alt="" width="24" height="24" />

Focus indicators on interactive icons show keyboard users where they are:

.icon-button:focus-visible {
    outline: 2px solid #ff6b6b;
    outline-offset: 2px;
}

For WCAG requirements on WordPress sites, see our WordPress accessibility and WCAG 2.2 guide.


#10. How to fix common SVG problems in UI

#Why SVG icons look distorted

The cause is usually a missing viewBox attribute or an aspect ratio conflict. Make sure SVG files have a viewBox and set the size in CSS. Do not hard-code both width and height when the icon has to scale.

#Why a Lottie animation does not show

Check that the path to the JSON file is correct and that the player script loaded. The browser console shows JavaScript errors or network errors (for example a 404, or a request blocked by the CSP policy).

#How to make SVG icons work in dark mode

Icons with colours hard-coded in the SVG will not adapt by themselves. Use currentColor in the fill and stroke attributes and set colours in CSS through the prefers-color-scheme query or a theme class.

#Why WordPress blocks SVG even with a plugin

Some hosting configurations and security plugins have their own file type restrictions, independent of WordPress settings. Check server-level restrictions and security plugin settings, and in Safe SVG also check whether your role is allowed to upload SVG.


#11. Checklist for moving from PNG icons to SVG

  • Run every icon through SVGO before it enters the theme or the sprite build.
  • Serve repeated icons from one <symbol> sprite and reference them with <use>.
  • Replace hard-coded colours with currentColor, and drive colours and dark mode through CSS custom properties.
  • Give each meaningful icon role="img" and an accessible name; hide decorative icons with aria-hidden="true" (never on focusable elements) or alt="" on <img>.
  • Allow SVG uploads only through a sanitizer (Safe SVG or enshrined/svg-sanitize) and only for trusted roles.
  • Check the licence of every set you add, and keep its licence notice or attribution comments in the files you ship.

#Summary

The avatars from the 2008 Smashing Magazine collection belong to design history, but the idea behind them, sharing graphic resources for free, is more widespread today than it was then. The icon libraries, avatar generators, and illustration collections of 2026 give projects of any size access to consistent visual systems.

A free resource still has a licence. Before you ship, check whether a set requires attribution (Font Awesome Free, the Fun Emoji style in DiceBear), whether keeping the licence file is enough (MIT, ISC, Apache 2.0), or whether it requires nothing (CC0).

A well-implemented icon is described or hidden from screen readers, inherits its colour through currentColor, passes through sanitization if it goes into the media library, and through SVGO before it reaches the theme.

#Sources

  1. W3C SVG 2 specification: https://www.w3.org/TR/SVG2/
  2. MDN Web Docs, SVG: https://developer.mozilla.org/en-US/docs/Web/SVG
  3. lottie-web (Airbnb): https://github.com/airbnb/lottie-web
  4. DiceBear, HTTP API and licences: https://www.dicebear.com/licenses/
  5. Humaaans: https://www.humaaans.com/
  6. unDraw: https://undraw.co/
  7. Safe SVG WordPress plugin: https://wordpress.org/plugins/safe-svg/
  8. Font Awesome Free, licence file: https://github.com/FortAwesome/Font-Awesome/blob/7.x/LICENSE.txt
  9. W3C WAI, decorative images: https://www.w3.org/WAI/tutorials/images/decorative/

Need help building a vector asset pipeline or icon system? Our WordPress development team can set up secure upload pipelines, optimize asset performance, and integrate custom icon systems. Contact us to discuss your project.

Next step

Turn the article into an actual implementation

This block strengthens internal linking and gives readers the most relevant next move instead of leaving them at a dead end.

Want this implemented on your site?

If you want to convert the article into a working site improvement, redesign, or build plan, I can define the scope and implement it.

Related cluster

Explore other WordPress services and knowledge base

Strengthen your business with professional technical support in key areas of the WordPress ecosystem.

Frequently asked questions

Practical answers to apply the topic in real execution.

Is it safe to allow SVG uploads in WordPress?#
Only if a strict XML sanitization process is active. By default, WordPress blocks SVG uploads because raw XML can contain malicious script tags.
Should I use SVG icon sprites or inline SVGs?#
Inline SVGs are ideal for interactive, styleable elements. Sprites are highly efficient for repetitive icons that do not require complex CSS hover states.
How can I change the color of an inline SVG?#
Set fill or stroke properties to currentColor in your CSS, and the SVG will inherit the text color of its parent HTML element.
What are variable icon fonts?#
Modern symbol libraries (like Material Symbols) that allow adjusting icon weight, optical size, and grade dynamically via CSS custom variables.
How do I optimize inline SVG accessibility for screen readers?#
For an icon that carries meaning, put role="img" on the outer svg element and give it an accessible name: aria-labelledby pointing at the id of a title element inside the SVG, or aria-label. A decorative icon next to text that says the same thing gets aria-hidden="true" instead.

Need an FAQ tailored to your industry and market? We can build one aligned with your business goals.

Let’s discuss

Related Articles