Icons
Icons are functional signals — not decoration. Used well, they reduce cognitive load, reinforce meaning, and help users navigate with confidence. This page covers when and how to use icons across Product and Editorial contexts, and how to implement Phosphor Icons in your project.
why phosphor?
An overview of why Caddy uses Phosphor Icons and how to get the most from the library.
Caddy uses Phosphor Icons — a flexible, open-source family of over 9,000 icons available in six weights: Thin, Light, Regular, Bold, Fill, and Duotone.
We chose Phosphor for three reasons:
Breadth and reliability.
The library is extensive and actively maintained, covering nearly every interface need without requiring custom icons.
Weight flexibility.
Six weights let you match visual density to context — lighter weights for editorial moments, heavier for small-scale UI — while keeping shapes instantly recognizable.
Shape integrity.
Despite weight variation, every Phosphor icon reads clearly at a glance. The geometry is designed for universality, not cleverness. This is why we use Phosphor rather than custom icons.
A 2013 benchmark study on icon comprehension across non-literate users found that the most effective icons use concrete, real-world shapes that transcend language and cultural context — Phosphor's core design principle. Accessible design benefits everyone.
Read the IAENG study →product icon guidance
Usage principles for designers building the Chipin product UI.
When to use icons
Icons belong in buttons, icon buttons, navigation elements, avatar-icons, status indicators, and task progress indicators. Their job is to help users understand the purpose of an interactive element or the state of the system — instantly, without reading.
DO
- ·Use icons in buttons to reinforce the action
- ·Use icons in nav to signal destination or mode
- ·Use icons to show system status (e.g. checkmark for complete)
- ·Use icons in avatar-icon components to represent user types
DON'T
- ·Use icons purely for decoration
- ·Use icons on success screens — use an illustration
- ·Use icons on product showcase screens — use an illustration
- ·Combine icons with labels that contradict the icon's meaning
Choosing icons
Choose icons that are instantly interpretable. If there's any question about what an icon means in context, choose a different one. For nav and buttons, the icon should be able to stand in for the label — not just accompany it. Use concrete, real-world imagery.
DO
ph-clock for event time
ph-calendar for date selection
ph-arrow-right for next / continue
DON'T
ph-alarm for event time
ph-calendar-check for unconfirmed events
ph-caret-right where more visual weight is needed
Weights
Default to Regular for all product UI. Adjust weight based on size and context. Within any group of related actions, use the same weight and size for all icons in that group.
| Example | Weight | When to use |
|---|---|---|
| Regular | Default for all product UI | |
| Bold | When icons appear at very small sizes (under 16px) | |
| Fill | Selected or active states (e.g. active nav icon) | |
| Light | Larger icons where less visual mass is appropriate | |
| Thin | Large illustrative icons only | |
| Duotone | Card buttons, data points, feature highlights only |
Hard rule
Never use Duotone in navigation elements. Reserve it for cards, data indicators, and feature highlights.
DO
- ·Use Regular across all items in a navigation bar
- ·Use Duotone in a feature card component
- ·Use Fill to show active / selected state
DON'T
- ·Mix Bold and Regular in the same nav bar
- ·Use Duotone in a sidebar nav item
- ·Use Fill for all icons regardless of state
Colour
Use icon semantic tokens as a starting point. Apply colour with intention — sentiment tokens communicate system state, not decoration.
| Example | Token | Use |
|---|---|---|
--icon-neutral |
Default colour for most UI contexts | |
--icon-positive |
Success states, confirmations | |
--icon-negative |
Error states, destructive actions | |
--icon-warning |
Caution, pending states |
Icons nested within interactive elements should use a content colour token to match adjacent text — e.g. --content-primary or --content-on-primary.
Applying secondary palette colours to icon and avatar-icon elements is one of the best ways to add personality without visual clutter — ideal for card buttons, feature lists, or anywhere a colour accent adds context.
Avoid using sentiment icon tokens (--icon-positive, --icon-negative) for decoration or branding. Use secondary colours instead.
editorial icon guidance
Usage principles for editorial, marketing, and branded content.
When to use icons
DO
- ·Pair an icon with a feature description to reinforce the message
- ·Use icons to anchor a proof point (e.g. beside Donation Management copy)
- ·Use icons to add visual rhythm to a list of features or benefits
DON'T
- ·Use icons as a substitute for illustration or UI screenshots
- ·Use icons in place of a headline image or hero graphic
- ·Use icons decoratively with no semantic relationship to the content
Weights
In editorial you have more creative freedom — use any weight that serves the design. The same consistency rule applies within sections: all icons in a proof-point row should share the same weight. Different sections can differ.
Colour
Use best judgement. Pull from the content palette for a grounded, typographic tone; the secondary palette to add personality; or icon semantic tokens for structured, system-like moments. Avoid using sentiment tokens (--icon-positive, --icon-negative) for promotion or decoration.
getting started with phosphor
Phosphor Icons is available for React, Vue, web components, and vanilla HTML. The primary path for Chipin's stack is @phosphor-icons/react.
React (Chipin Stack)
Install:
npm install @phosphor-icons/react
Basic usage:
import { CalendarCheck, Bell, ArrowRight } from "@phosphor-icons/react";
<CalendarCheck />
<Bell size={24} weight="bold" color="var(--icon-neutral)" />
Set global defaults via context:
import { IconContext } from "@phosphor-icons/react";
<IconContext.Provider value={{ size: 20, weight: "regular" }}>
<App />
</IconContext.Provider>
Props reference:
| Prop | Type | Default | Description |
|---|---|---|---|
size |
number | string |
1em |
Width and height |
weight |
"thin" | "light" | "regular" | "bold" | "fill" | "duotone" |
"regular" |
Icon weight / style |
color |
string |
currentColor |
Stroke / fill — accepts CSS vars |
mirrored |
boolean |
false |
Flip horizontally for RTL |
alt |
string |
— | Accessible alt text |
Next.js bundle optimisation:
// next.config.js
module.exports = {
experimental: {
optimizePackageImports: ["@phosphor-icons/react"],
},
};
SSR / Server Components — import from the /ssr sub-path:
import { BellIcon } from "@phosphor-icons/react/ssr";
Vanilla HTML
Load only the weights you need (recommended):
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@phosphor-icons/web@2.1.2/src/regular/style.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@phosphor-icons/web@2.1.2/src/fill/style.css" />
<i class="ph ph-calendar-check"></i>
<i class="ph-fill ph-heart" style="color: var(--icon-positive)"></i>
Load all weights via a single script (~3 MB — avoid in production):
<script src="https://cdn.jsdelivr.net/npm/@phosphor-icons/web@2.1.2"></script>
Weight class prefix reference:
| Example | Weight | Class prefix |
|---|---|---|
| Regular | ph |
|
| Bold | ph-bold |
|
| Fill | ph-fill |
|
| Light | ph-light |
|
| Thin | ph-thin |
|
| Duotone | ph-duotone |
Applying Caddy Tokens
Always apply colour using CSS custom properties — never hardcoded hex values.
// ✅ Token-driven
<Bell color="var(--icon-neutral)" />
<CheckCircle color="var(--icon-positive)" />
// ✅ Inherits currentColor from parent
<span style={{ color: "var(--content-primary)" }}>
<ArrowRight />
</span>
// ❌ Avoid — hardcoded colour
<Bell color="#065c3e" />
Recommended sizes by context:
| Context | Size |
|---|---|
| Inline with body text | 1em |
| Button icons | 20px |
| Navigation icons | 24px |
| Avatar-icon | 20–24px |
| Card / feature icon | 32–40px |
| Duotone / editorial | 40–56px |