Build a Local Font Viewer Chrome Extension: Complete Developer Guide

19 min read

Build a Local Font Viewer Chrome Extension: Complete Developer Guide

Build a Local Font Viewer Chrome Extension: Complete Developer Guide

Chrome extensions have revolutionized the way we interact with web browsers, adding powerful functionality that extends far beyond the default browsing experience. Among the most useful extensions for designers and developers are those that provide access to local system fonts. In this comprehensive guide, we will walk you through the entire process of building a local font viewer Chrome extension that can enumerate and display all fonts installed on your system directly within the browser.

The ability to preview system fonts without leaving your browser is invaluable for web designers, graphic artists, and developers who frequently work with typography. Whether you need to quickly check which fonts are available for a new project or want to compare how different fonts look in a web context, having a dedicated font browser extension streamlines your workflow significantly.

This guide assumes you have basic familiarity with HTML, CSS, and JavaScript, though we will explain all the Chrome-specific APIs and concepts in detail. By the end of this tutorial, you will have a fully functional local font viewer extension that you can use and customize to meet your specific needs.


Understanding Chrome Extension Architecture

Before diving into the implementation, it is essential to understand how Chrome extensions are structured. Chrome extensions are essentially web applications that run in a controlled environment within the Chrome browser. They consist of several components that work together to provide functionality.

Core Extension Components

Every Chrome extension requires a manifest file, which serves as the configuration document that tells Chrome about the extension’s permissions, files, and capabilities. The manifest.json file is the backbone of your extension, defining everything from the extension name and version to the specific permissions it requires to function.

Content scripts are JavaScript files that run in the context of web pages, allowing your extension to interact with the content of visited pages. These scripts can read and modify page content, but they operate in an isolated world, meaning they cannot access variables or functions defined by the web page itself.

Background scripts, also known as service workers in Manifest V3, run in the background and handle events like browser notifications, alarms, and cross-page communication. They serve as the central hub for your extension’s logic, coordinating between different components.

The popup is the small window that appears when you click the extension icon in the Chrome toolbar. This is where users interact with your extension most directly, making the popup design crucial for user experience.

Manifest V3: The Current Standard

Google introduced Manifest V3 to improve security, performance, and user privacy in Chrome extensions. Unlike the older Manifest V2, version 3 imposes stricter requirements on how extensions can function. Notably, background scripts are now service workers that cannot run continuously, and remote code execution is prohibited.

For our local font viewer extension, Manifest V3 presents both opportunities and challenges. The primary challenge is that accessing system fonts requires careful handling of permissions, as extensions no longer have unrestricted access to browser APIs. However, the benefits of improved security and performance make this transition worthwhile.


Setting Up Your Development Environment

Before writing any code, you need to set up your development environment properly. This involves creating the project structure and configuring the necessary files for your extension.

Project Structure

Create a new folder for your extension project and organize it with the following structure:

local-font-viewer/
├── manifest.json
├── popup.html
├── popup.js
├── popup.css
├── background.js
├── content.js
└── icons/
    ├── icon16.png
    ├── icon48.png
    └── icon128.png

This structure keeps your files organized and makes it easier to maintain and update your extension over time. Each file serves a specific purpose in the overall functionality of the extension.

Creating the Manifest File

The manifest.json file is your first and most important task. It defines the extension’s metadata and permissions. For a local font viewer extension, we need to request specific permissions to access font-related APIs and display the popup.

{
  "manifest_version": 3,
  "name": "Local Font Viewer",
  "version": "1.0",
  "description": "View and preview all system fonts directly in your browser",
  "permissions": [
    "scripting"
  ],
  "action": {
    "default_popup": "popup.html",
    "default_icon": {
      "16": "icons/icon16.png",
      "48": "icons/icon48.png",
      "128": "icons/icon128.png"
    }
  },
  "background": {
    "service_worker": "background.js"
  },
  "icons": {
    "16": "icons/icon16.png",
    "48": "icons/icon48.png",
    "128": "icons/icon128.png"
  }
}

This manifest declares that our extension uses Manifest V3, requires the scripting permission (needed for content scripts), and defines the popup and background service worker.


The popup is the primary user interface for your local font viewer extension. It needs to be clean, intuitive, and responsive. Users should be able to quickly browse through their available fonts and preview them in different sizes and contexts.

HTML Structure

Create the popup.html file with a well-structured layout that includes controls for font selection, size adjustment, and preview options:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Local Font Viewer</title>
  <link rel="stylesheet" href="popup.css">
</head>
<body>
  <div class="container">
    <header>
      <h1>Local Font Viewer</h1>
      <p class="subtitle">Browse your system fonts</p>
    </header>
    
    <div class="controls">
      <div class="search-box">
        <input type="text" id="fontSearch" placeholder="Search fonts...">
      </div>
      <div class="size-control">
        <label for="fontSize">Preview Size:</label>
        <input type="range" id="fontSize" min="12" max="72" value="24">
        <span id="sizeValue">24px</span>
      </div>
    </div>
    
    <div class="font-list" id="fontList">
      <div class="loading">Loading fonts...</div>
    </div>
    
    <div class="preview-section" id="previewSection">
      <h2>Font Preview</h2>
      <textarea id="previewText" placeholder="Type to preview text...">The quick brown fox jumps over the lazy dog</textarea>
      <div id="previewDisplay" class="preview-display"></div>
    </div>
  </div>
  
  <script src="popup.js"></script>
</body>
</html>

This HTML structure provides a search box for filtering fonts, a slider for adjusting preview size, a list of available fonts, and a preview area where users can type custom text to see how it looks in different fonts.

Styling the Popup

The CSS file should make your extension visually appealing while maintaining good usability. Use a clean, modern design that complements Chrome’s interface:

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  width: 400px;
  min-height: 500px;
  background: #f5f5f5;
  color: #333;
}

.container {
  padding: 16px;
}

header {
  text-align: center;
  margin-bottom: 16px;
  padding-bottom: 12px;
  border-bottom: 1px solid #e0e0e0;
}

header h1 {
  font-size: 18px;
  font-weight: 600;
  color: #1a73e8;
}

.subtitle {
  font-size: 12px;
  color: #666;
  margin-top: 4px;
}

.controls {
  margin-bottom: 16px;
}

.search-box input {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid #ddd;
  border-radius: 6px;
  font-size: 14px;
}

.search-box input:focus {
  outline: none;
  border-color: #1a73e8;
  box-shadow: 0 0 0 2px rgba(26, 115, 232, 0.2);
}

.size-control {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 12px;
  font-size: 13px;
}

.size-control input[type="range"] {
  flex: 1;
}

.font-list {
  max-height: 200px;
  overflow-y: auto;
  border: 1px solid #e0e0e0;
  border-radius: 6px;
  background: white;
  margin-bottom: 16px;
}

.font-item {
  padding: 10px 12px;
  cursor: pointer;
  border-bottom: 1px solid #f0f0f0;
  transition: background 0.15s;
}

.font-item:hover {
  background: #f8f9fa;
}

.font-item.selected {
  background: #e8f0fe;
  color: #1a73e8;
}

.font-item:last-child {
  border-bottom: none;
}

.preview-section h2 {
  font-size: 14px;
  margin-bottom: 8px;
  color: #444;
}

#previewText {
  width: 100%;
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 6px;
  resize: vertical;
  min-height: 60px;
  font-size: 14px;
  margin-bottom: 12px;
}

.preview-display {
  padding: 16px;
  background: white;
  border: 1px solid #e0e0e0;
  border-radius: 6px;
  min-height: 80px;
  word-wrap: break-word;
}

.loading {
  padding: 20px;
  text-align: center;
  color: #666;
}

::-webkit-scrollbar {
  width: 8px;
}

::-webkit-scrollbar-track {
  background: #f1f1f1;
}

::-webkit-scrollbar-thumb {
  background: #c1c1c1;
  border-radius: 4px;
}

::-webkit-scrollbar-thumb:hover {
  background: #a1a1a1;
}

This CSS provides a clean, modern interface that follows Material Design principles. The styling includes proper spacing, hover states, and scrollbar styling for a polished look.


Implementing the Core Functionality

Now comes the most interesting part: actually getting the list of system fonts and displaying them in the extension. This requires JavaScript to communicate between different parts of the extension and handle user interactions.

The Background Script

The background script serves as the bridge between different extension components and handles the initial font enumeration. In Manifest V3, we use the scripting API to execute code that can access font information:

// background.js
chrome.runtime.onInstalled.addListener(() => {
  console.log('Local Font Viewer extension installed');
});

// Handle messages from popup
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.action === 'getFonts') {
    // Get fonts using the Fonts API via executeScript
    chrome.scripting.executeScript({
      target: { tabId: sender.tab.id },
      function: getSystemFonts
    }, (results) => {
      if (results && results[0] && results[0].result) {
        sendResponse({ fonts: results[0].result });
      } else {
        // Fallback fonts if API is not available
        sendResponse({ 
          fonts: getDefaultFonts() 
        });
      }
    });
    return true; // Keep message channel open for async response
  }
});

// This function runs in the context of the active page
function getSystemFonts() {
  // Try to use the Chrome Fonts API if available
  if (window.chrome && window.chrome.fontAccess) {
    return window.chrome.fontAccess.query({}) 
      .then(fontAccess => fontAccess.fonts.map(f => f.family))
      .catch(() => getDefaultFonts());
  }
  return getDefaultFonts();
}

// Default font list as fallback
function getDefaultFonts() {
  return [
    'Arial', 'Arial Black', 'Comic Sans MS', 'Courier New',
    'Georgia', 'Impact', 'Lucida Console', 'Lucida Sans Unicode',
    'Palatino Linotype', 'Segoe UI', 'Tahoma', 'Times New Roman',
    'Trebuchet MS', 'Verdana', 'Helvetica', 'Monaco',
    'Menlo', 'Consolas', 'Source Code Pro', 'Fira Code'
  ];
}

The background script attempts to use the Chrome Fonts API to get the actual system fonts. If that API is not available (which is common in regular web pages), it falls back to a default list of common fonts.

The Popup Script

The popup script handles user interactions and updates the UI based on user input:

// popup.js
document.addEventListener('DOMContentLoaded', () => {
  const fontList = document.getElementById('fontList');
  const fontSearch = document.getElementById('fontSearch');
  const fontSize = document.getElementById('fontSize');
  const sizeValue = document.getElementById('sizeValue');
  const previewText = document.getElementById('previewText');
  const previewDisplay = document.getElementById('previewDisplay');
  
  let allFonts = [];
  let selectedFont = null;
  
  // Load fonts when popup opens
  loadFonts();
  
  async function loadFonts() {
    try {
      // Request fonts from background script
      const response = await chrome.runtime.sendMessage({ action: 'getFonts' });
      
      if (response && response.fonts) {
        allFonts = response.fonts.sort();
        renderFontList(allFonts);
      } else {
        showError('Unable to load fonts');
      }
    } catch (error) {
      console.error('Error loading fonts:', error);
      showError('Error loading fonts');
    }
  }
  
  function renderFontList(fonts) {
    fontList.innerHTML = '';
    
    fonts.forEach(font => {
      const fontItem = document.createElement('div');
      fontItem.className = 'font-item';
      fontItem.textContent = font;
      fontItem.style.fontFamily = font;
      
      fontItem.addEventListener('click', () => {
        // Remove selected class from all items
        document.querySelectorAll('.font-item').forEach(item => {
          item.classList.remove('selected');
        });
        // Add selected class to clicked item
        fontItem.classList.add('selected');
        selectedFont = font;
        updatePreview();
      });
      
      fontList.appendChild(fontItem);
    });
    
    // Auto-select first font if available
    if (fonts.length > 0 && !selectedFont) {
      const firstFont = fontList.querySelector('.font-item');
      if (firstFont) {
        firstFont.click();
      }
    }
  }
  
  function updatePreview() {
    if (!selectedFont) return;
    
    const size = fontSize.value;
    const text = previewText.value || 'The quick brown fox jumps over the lazy dog';
    
    previewDisplay.style.fontFamily = selectedFont;
    previewDisplay.style.fontSize = size + 'px';
    previewDisplay.textContent = text;
  }
  
  function showError(message) {
    fontList.innerHTML = `<div class="loading" style="color: red;">${message}</div>`;
  }
  
  // Search functionality
  fontSearch.addEventListener('input', (e) => {
    const searchTerm = e.target.value.toLowerCase();
    const filteredFonts = allFonts.filter(font => 
      font.toLowerCase().includes(searchTerm)
    );
    renderFontList(filteredFonts);
  });
  
  // Size control
  fontSize.addEventListener('input', (e) => {
    sizeValue.textContent = e.target.value + 'px';
    updatePreview();
  });
  
  // Preview text update
  previewText.addEventListener('input', () => {
    updatePreview();
  });
});

This popup script handles loading fonts, displaying them in a scrollable list, filtering fonts based on search input, and updating the preview when users select different fonts or change the preview text and size.


Advanced Features and Enhancements

Once you have the basic functionality working, you can enhance your local font viewer extension with additional features that make it even more useful for designers and developers.

Adding Font Information Display

One valuable enhancement is displaying additional information about each font, such as whether it supports various character sets, its style (serif, sans-serif, monospace), and other metadata. You can implement this by extending the font enumeration logic to include more details about each font.

Font Comparison Feature

Allow users to compare multiple fonts side by side by selecting several fonts and displaying them together. This is particularly useful when choosing between similar fonts for a design project.

Export Functionality

Add the ability to export font lists or selected fonts for use in design tools. You can generate CSS, JSON, or other formats that developers can easily import into their projects.

Favorites and History

Implement a system for users to mark favorite fonts and view their recently previewed fonts. This helps users quickly access their preferred fonts without searching through the entire list every time.


Testing Your Extension

Before publishing your extension, thorough testing is essential to ensure it works correctly across different scenarios and user configurations.

Loading Unpacked Extensions

To test your extension in Chrome, navigate to chrome://extensions/ and enable Developer mode. Then click “Load unpacked” and select your extension’s folder. The extension icon should appear in your toolbar, and you can interact with it to verify functionality.

Testing Different Scenarios

Test your extension with various font configurations, including systems with many fonts, few fonts, and unusual font installations. Verify that the search functionality works correctly, the preview updates properly, and the extension handles errors gracefully.

Performance Considerations

Ensure your extension loads quickly and doesn’t consume excessive memory. The font enumeration should be efficient, and the UI should remain responsive even with large font lists.


Publishing Your Extension

Once you have thoroughly tested your extension and are satisfied with its functionality, you can publish it to the Chrome Web Store for others to discover and use.

Creating Developer Account

To publish Chrome extensions, you need a Google Developer account. Visit the Chrome Web Store developer dashboard and complete the registration process, which includes a one-time registration fee.

Preparing Store Listing

Create compelling store listing materials, including a clear description, screenshots, and a distinctive icon. Your description should highlight the key features and benefits of your local font viewer extension.

Uploading and Publishing

Package your extension using the Chrome Web Store developer dashboard, fill in the required information, and submit for review. Google reviews extensions to ensure they meet security and policy requirements.


Conclusion

Building a local font viewer Chrome extension is an excellent project for developers looking to learn about Chrome extension development while creating a genuinely useful tool. Throughout this guide, we have covered the essential components: manifest configuration, popup design, background script logic, and the JavaScript needed to bring everything together.

The extension you build provides immediate value by allowing designers and developers to browse and preview their system fonts without leaving the browser. This streamlines workflows and helps make informed typography decisions for web projects.

As you continue to develop and refine your extension, consider adding the advanced features we discussed, such as font comparison, export functionality, and favorites. These enhancements can differentiate your extension in the Chrome Web Store and provide even more value to users.

Chrome extension development offers endless possibilities for creating tools that enhance the browsing experience. The local font viewer is just one example of how you can leverage browser APIs to build practical applications. With the foundation you have gained from this guide, you are well-equipped to explore more advanced extension development projects and bring your ideas to life.

No previous article
No next article