One concept, three icon names.
An app knows it wants to draw a house. What a house is called depends on who is
drawing it: Bootstrap Icons says house, SF Symbols says house, Material Symbols
says home. Unicon holds that translation, so a Rails app can name the concept once
and let the web page, the iOS client and the Android client each draw it from the set
they already ship.
Unicon.fetch :house
# => { bootstrap: 'house', ios: 'house', android: 'home' }
Unicon[:contact]
# => { bootstrap: 'person-rolodex', ios: 'person.crop.circle', android: 'contacts' }Unicon[] and Unicon.fetch are the same method; [] is the shorter spelling.
The keys are symbols and the names are frozen strings: a name is on its way into
"bi bi-#{icon}" or into JSON either way, and person.crop.circle is a string long
before it is a symbol.
gem 'unicon'Every concept carries exactly three keys, and never fewer:
| Key | Design system | License |
|---|---|---|
:bootstrap |
Bootstrap Icons | MIT |
:ios |
SF Symbols | Apple, Apple platforms only |
:android |
Material Symbols | Apache 2.0 |
The keys are named after the client that draws the icon rather than after the system,
because that is the question a caller is answering. :ios always holds an SF Symbols
name and :android always holds a Material Symbols name — nothing else is meant by
them.
Unicon.fetch :nothing_by_this_name
# => { bootstrap: 'circle', ios: 'circle', android: 'circle' }A caller rendering whatever it is handed never has to check first, and never draws a
blank. A concept may be given as a string, so Unicon['contact'] works too.
Unicon.icons # => [:account, :airplane, :alarm, :archive, :award, :badge, ...] 127
Unicon.names # => [:account, :address, :agent, :airplane, :alarm, :alert, ...] 303
Unicon.actions # => [:arrow_down, :arrow_left, :arrow_right, :arrow_up, ...] 42Unicon.iconsis one name per distinct picture. No two entries draw the same thing, which is what makes it the list to show somebody choosing an icon.Unicon.namesis the union of all three groups — the icons, the actions and the model synonyms — and so every name that resolves. Ask for it to find out whether a name draws something; ask foriconswhen somebody is choosing one.Unicon.actionsis the doing words:close,pencil, every chevron. A toolbar needs them andUnicon[:close]answers, but nobody has a Close model, soiconsleaves them out on purpose.
All three are frozen, sorted arrays of symbols; only what fetch returns is strings.
Unicon::ICONS, Unicon::TABLE and Unicon::ACTIONS are the tables behind them, for a
caller who wants the pictures as well as the names.
A host app looks an icon up by the name of the model it is rendering, and the more names
answer, the less any app has to declare. 134 of them borrow the icons of a concept —
:user takes the person, :zip takes the pin — and the Aliases column of the table below
says which took which. They resolve without swelling the list a picker offers.
Four borrow an action rather than a thing, so they are the four the table cannot show:
:export takes the download, :import the upload, :sync the refresh, :subscription
the repeat. Each still answers to Unicon[] like any other name.
A concept is listed only when all three systems have a sensible icon for it. Where one of the three has none, the concept is left out rather than filled in with something close — an agent is a badge rather than a robot here, because Bootstrap and Material both draw a robot and SF Symbols has none.
The SF Symbols are shown under section 2.11 of the Xcode and Apple SDKs Agreement, which licenses them for developing applications for Apple-branded products, and this repository is part of building one. They may not be redistributed for any other purpose, so anyone reusing this gem outside that context should show the names alone.
The cells hold the icon and not its name, so the table stays readable at 127 rows. The
name each system uses is on the icon: hover it, or reach it with a screen reader, and
Unicon[:search][:ios] reads back as magnifyingglass.
The 42 action icons are not in the table. They are fetchable — Unicon[:pencil] answers
— but they name what an app does rather than what it has, and this table is Unicon.icons
drawn out. Unicon.actions names them all.
A dashed circle is a symbol newer than the macOS that drew this table, which knows the SF Symbols 5 set. Hover it for the name, which resolves in an app built against a newer SDK.
Every name in the table is checked, by rake validate, against the list its own design
system publishes. The lists are vendored under data/, so the check needs no network
and runs as part of the default rake task.
data/bootstrap-icons.txt— 2,078 names, fromfont/bootstrap-icons.jsonat tagv1.13.1of twbs/icons.data/material-symbols.txt— 3,896 names, from Google's metadata endpoint, keeping only what Material Symbols Outlined actually ships. The endpoint also lists legacy Material Icons, such aslaptopandsmartphone, which no longer resolve as symbols.data/sf-symbols.txt— 8,295 names, fromMetadata/name_availability.plistinsideSF Symbols.app, filtered to names whose iOS availability is 18.3 or earlier. The plist gives each release an iOS version, and that version is the constraint, so the filter reads it rather than the release year. A name added in iOS 18.4 or later is left out and cannot enter the table.
All three columns show artwork vendored under vendor/, so the table renders whether or
not a CDN answers, and each directory carries the license its artwork came with.
Bootstrap Icons and Material Symbols are downloaded from their published sources, and
differ from upstream in two attributes: a gray fill, so a row reads on a light and a dark
theme alike, and a 20px box, so the columns line up. Apple publishes no files to fetch,
so tools/sf_symbols.swift asks macOS to draw each symbol and writes a PNG in the same
gray and the same box.
That rendering is done by the machine running rake readme, and a machine only knows the
symbols its own system shipped with. Anything newer gets assets/undrawn.svg — a dashed
circle of our own drawing, not Apple's — with the symbol's name still on it to hover.
One row is in that state today: :receipt, whose SF symbol arrived in iOS 18.2, drawn on
a Mac running macOS 14.3. rake validate has already proved every name real, which is
why a symbol this Mac cannot draw is reported rather than fatal.
MIT. See LICENSE.txt.