How to generate .icon-* css classes from icon names
- Step 1Paste your icon names — One name per line in the textarea. Whitespace is trimmed and blank lines are dropped, so a tidy list and a messy one produce the same clean CSS.
- Step 2Set the start codepoint to match your font — If your icon font's first glyph is at U+E000, keep the default. If it starts higher, type that hex in the Start field (a `U+` prefix is fine). The escapes must match where the glyphs actually sit in the font.
- Step 3Generate — Click Generate. The tool computes the rules from your names and start — no upload, no wait for processing.
- Step 4Copy the CSS rules — Each line is `.icon-<slug>::before { content: "\<hex>"; }`. Paste the whole block into your icon stylesheet, after the `@font-face` and base `.icon` rule that sets `font-family`.
- Step 5Add the shared base rule yourself — The tool emits only the per-icon `content` rules. You still need a base rule that applies your icon `font-family` (and `font-style`, `display`, etc.) to elements using these classes — that part is your stylesheet, not the tool's output.
- Step 6Wire the manifest into your build — Grab the JSON manifest from the trailing comment to auto-generate an icon cheat-sheet or to assert in CI that every class in your CSS has a matching codepoint.
From name to CSS rule
How a single input line becomes a CSS rule and the other manifest fields, at the default start.
| Input name | cssClass | Emitted rule | cssEscape |
|---|---|---|---|
home | .icon-home | .icon-home::before { content: "\e000"; } | \e000 |
Arrow Left | .icon-arrow-left | .icon-arrow-left::before { content: "\e001"; } | \e001 |
user_profile | .icon-user-profile | .icon-user-profile::before { content: "\e002"; } | \e002 |
check.circle | .icon-check-circle | .icon-check-circle::before { content: "\e003"; } | \e003 |
Slugification rules
Exactly how a name becomes a class selector. Applied character-by-character, then lowercased.
| In the name | Becomes | Example |
|---|---|---|
| Uppercase letter | Lowercased | Home → home |
| Space | Hyphen | arrow left → arrow-left |
| Underscore | Hyphen | user_x → user-x |
| Dot / slash / punctuation | Hyphen | check.circle → check-circle |
| Letters, digits, existing hyphen | Kept as-is | icon-2fa → icon-2fa |
What the tool emits vs. what you still write
The output covers the per-icon content rules only. The surrounding stylesheet plumbing is yours.
| Piece | From this tool? | Where it comes from |
|---|---|---|
@font-face loading the icon font | No | Your stylesheet / font-face-generator |
Base .icon { font-family: ... } rule | No | Your stylesheet |
.icon-name::before { content } rules | Yes | This tool |
| JSON manifest of the mapping | Yes | This tool (in a trailing comment) |
Cookbook
Copy-paste recipes for turning names into a working icon stylesheet. The tool supplies the content rules; the framing rules are shown so the result actually renders.
A complete icon stylesheet, framing included
ExampleThe tool emits only the bottom block. Wrap it with your @font-face and base rule to get a renderable stylesheet.
/* you write this part */
@font-face {
font-family: "myicons";
src: url("/fonts/myicons.woff2") format("woff2");
}
[class^="icon-"]::before {
font-family: "myicons";
font-style: normal;
}
/* the tool emits this part */
.icon-home::before { content: "\e000"; }
.icon-user::before { content: "\e001"; }
.icon-search::before { content: "\e002"; }Matching a font that starts at U+F000
ExampleIf your existing icon font places its first glyph at U+F000 (some Font Awesome-style sets do), set the start so the escapes line up.
Names:
home
user
Start: F000
Emitted rules:
.icon-home::before { content: "\f000"; }
.icon-user::before { content: "\f001"; }Spotting a slug collision before it ships
ExampleTwo differently-spelled names can produce the same selector. Both still get unique codepoints, but the later CSS rule wins in the cascade — so one icon renders the wrong glyph.
Names:
Arrow Left
arrow-left
Emitted rules:
.icon-arrow-left::before { content: "\e000"; }
.icon-arrow-left::before { content: "\e001"; } <- same selector!
In the cascade the \e001 rule wins. Rename one to fix.Generating a docs cheat-sheet from the manifest
ExampleParse the manifest comment in a build script to render an HTML table of class + glyph for your design system docs.
// pseudo build step
const { mappings } = parseManifestComment(css);
for (const m of mappings) {
// m.cssClass = '.icon-home', m.hex = 'U+E000'
row(m.name, m.cssClass, m.hex, m.htmlEntity);
}Asserting CSS and manifest agree in CI
ExampleCatch a hand-edited stylesheet drifting from the mapping by checking every class in the CSS appears in the manifest.
// CI guard
const classesInCss = extractIconSelectors(stylesheet);
const classesInManifest = new Set(mappings.map(m => m.cssClass));
for (const c of classesInCss) {
if (!classesInManifest.has(c)) fail(`stray icon class: ${c}`);
}Edge cases and what actually happens
Every row below was probed against the live API. Some documented requirements (alphabetical axis order, numerical tuple order) are not actually enforced in practice — useful to know if you've been blaming the wrong thing for a 400.
Empty name list
Error — rejectedAn empty textarea throws Paste at least one icon name. There is no implicit default set of icons.
Two names slugify to the same selector
Caution — cascade collisionArrow Left and arrow-left both become .icon-arrow-left, each with its own codepoint. Both rules ship; the later one wins the cascade, so one element renders the wrong glyph. The tool does not warn — dedupe class names before pasting.
Name is only punctuation
Caution — degenerate class*** slugifies to .icon---- (one hyphen per character). It compiles but is unreadable and easy to collide. Give every glyph a word-based name.
Numeric-leading name
Handled — valid selector2fa becomes .icon-2fa. Because the class is prefixed with icon-, the selector never starts with a digit, so it stays a valid CSS class even for numeric names.
Start outside U+E000–U+F8FF
Error — invalid rangeAny start below U+E000 or above U+F8FF throws PUA start must be a hex codepoint in the U+E000–U+F8FF range. The escapes must point into the Private Use Area.
More rules than fit before U+F8FF
Error — out of spaceIf the list runs past U+F8FF the tool throws an out-of-space error at the overflow codepoint instead of emitting an invalid rule. Reduce the list or lower the start.
You only paste the content rules and nothing renders
Expected — missing framingThe output sets content but not font-family. Without a base rule applying your icon font to the element, the browser renders the raw PUA character (a box or blank), not the glyph. Add the @font-face and base rule yourself.
CSS escape vs. JS string escaping
Caution — contextThe CSS escape is a single backslash (\e000) for stylesheets. If you copy a rule into a JS template literal or JSON, the backslash itself may need escaping (\\e000). The CSS file is correct as-is; the doubling is only a host-language concern.
Re-running after reordering names
Caution — selectors shiftReordering the list reassigns codepoints by position, so a class that was \e003 may become \e001. Existing markup using the class still works, but any hard-coded escapes elsewhere break. Append, don't reorder.
Trailing blank line in the list
Handled — ignoredA blank final line is filtered out and does not produce an empty .icon-::before rule or consume a codepoint.
Frequently asked questions
Does the output include the @font-face rule?
No. It emits only the per-icon ::before { content } rules and a JSON manifest. You supply the @font-face and the base rule that sets the icon font-family. Build the @font-face with font-face-generator if you need one.
Why isn't my icon showing even though the class is right?
Setting content alone is not enough — the element must also use your icon font's font-family. Without that base rule the browser shows the raw Private Use Area character, which has no standard glyph.
How are class names derived from my icon names?
.icon- plus the name lowercased, with every character outside [a-z0-9-] replaced by a hyphen. Spaces, underscores, and dots all become hyphens.
Can two names produce the same class?
Yes, and the tool won't stop you. Arrow Left and arrow-left both yield .icon-arrow-left. Each still gets a unique codepoint, but the duplicate selector causes a cascade collision. Rename before pasting.
Why is the CSS escape lowercase?
The tool builds cssEscape as a backslash plus lowercase hex (\e000). CSS hex escapes are case-insensitive, so lowercase is purely a style choice and renders identically.
Does it generate the icon font itself?
No — it never touches a font binary or glyph outlines. It maps names to codepoints and writes CSS. The glyphs come from the font you already load.
What start value should I use?
Use whatever codepoint your icon font's first glyph occupies — usually U+E000. If your font starts elsewhere in U+E000–U+F8FF, set the Start field to match so the escapes line up.
Can I get SCSS or a CSS variable map instead?
The tool emits plain CSS rules plus a JSON manifest. You can transform the manifest into SCSS maps or custom properties in your build. For generating CSS custom properties from a font, see css-variable-generator-font.
Is there a limit on how many classes I can generate?
The practical limit is the BMP PUA size: 6,400 codepoints (U+E000–U+F8FF). Past that the tool throws an out-of-space error rather than emitting invalid escapes.
Does it deduplicate identical names?
No. Every line becomes a rule, even exact duplicates. Clean your list first if you want one rule per icon.
How do I keep classes stable when adding icons later?
Append new names to the end of the list so existing codepoints and escapes don't shift. Reordering renumbers everything.
How can I verify my icon font actually contains these glyphs?
Inspect the font with glyph-inspector or check covered ranges with character-coverage-map. This tool only produces the CSS side of the mapping.
Privacy first
Every JAD Font tool runs entirely in your browser using opentype.js and the wawoff2 WASM Brotli encoder. Your fonts never leave your device — verified by zero outbound network requests during processing.