Authoring icons
Icons in css-is-awesome are inline SVGs tinted by currentColor, so every glyph re-skins with the active theme automatically — no icon font, no sprite sheet, no runtime tint pipeline.
Approach
The icon system is folder-based and mixin-first. Drop an SVG into public/icons/, reference it with @include m.svg(name), and it inherits the parent's color. The same file is also usable as a plain inline <svg> in React components — that is the pattern the docs site itself follows. The principles are the same either way:
- Every shipped icon paints with
stroke="currentColor"(andfill="none"), so text color drives icon color. No hardcoded hex values live in the SVG source. - The default icon size is
24px— both the SCSS config ($theme-icon-sizeinscss/theme/_icons.scss) and every shipped SVG uses a24 × 24viewBox. Size is set per call site viawidth/height(or via the mixin's$sizeargument). - Decorative icons get
aria-hidden="true". Meaningful icons userole="img"plus<title>. Icon-only buttons put the accessible name on the<button>itself. - No icon font in the default path — the SCSS layer ships an opt-in Font Awesome integration for projects already on FA, but the recommended default is inline SVG. Inline SVG avoids the accessibility and tree-shaking penalties of icon fonts.
Using icons
There is no <Icon> React component in the docs site — components inline the SVG directly (see src/components/SearchBar/SearchBar.tsx for the canonical example). The same pattern works in any app. From SCSS, the mixin API in scss/_icons.scss is the primary surface:
// SCSS — inline icon that tints with text color @use 'cia/scss/mixins' as m; .btn-close { @include m.svg(close); // resolves to /icons/x.svg via alias color: var(--color-danger); } .btn-save { @include m.svg-text(check); // icon + label, 0.5em gap } .logo { @include m.svg-bg(brand-mark, 48px); // multi-color, no tint }
From React, inline the SVG and let currentColor do the work. This is exactly how the real SearchBar component renders its magnifier:
// Decorative: aria-hidden, inherits parent color <span aria-hidden="true"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" width="24" height="24"> <circle cx="10.5" cy="10.5" r="7.5" /> <path d="M16 16l5 5" /> </svg> </span>
Authoring a new icon
- Start with a
24×24viewBox. Every icon inpublic/icons/usesviewBox="0 0 24 24". Matching the existing grid keeps strokes and optical weight consistent across the pack. - Use
fill="none",stroke="currentColor",stroke-width="1.6". The shipped icons use strokes between1.6and1.8withstroke-linecap="round"andstroke-linejoin="round". Stick to that palette unless you have a reason — mixed stroke weights look ragged in a toolbar. - Keep paths minimal. Icons render at 16–24px most of the time. Decorative detail (fine hatching, thin highlights, micro curves) is lost below 24px. Collapse compound shapes to single paths where possible — the shipped
check.svgis a single<path d="M4 12l5 5L20 6" />. - Name icons by semantic role. Use
check,close,chevron-down— notlittle-xorblue-arrow. When the visual metaphor changes, only the file changes; every call site stays correct. Use$theme-icon-svg-aliasinscss/theme/_icons.scsswhen you want a semantic call site (delete) to resolve to a neutral filename (trash.svg) — the default map already shipsdelete → trashandclose → x. - Drop the file into
public/icons/and wire it in. No registry, no import list. From SCSS:@include m.svg(my-icon)now works. From React, import the raw SVG markup or copy/paste it inline. For a full authoring reference, seepublic/icons/README.md.
Sizing and color
Size is set at the call site via width / height (or the $size argument on the mixins). Color is never set on the SVG — it reads currentColor and follows whatever color the parent has. That is the entire coupling between icons and themes: when the theme picker changes the foreground color token, every icon on the page re-tints in the same frame.
// The SVG stroke is currentColor, so it inherits the button's ink. // Re-skin via the theme picker — the icon re-colors on the same frame. <button class="cia-btn-primary"> <span aria-hidden="true"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" width="16" height="16"> <path d="M4 12l5 5L20 6" /> </svg> </span> Save </button>
If you need an icon that must not tint — a brand mark, a multi-color illustration — use m.svg-bg instead of m.svg. It renders the file as a plain background-image, preserving the baked-in palette.
Accessibility checklist
- Decorative icons. Add
aria-hidden="true"to the<svg>(or its wrapping<span>) and skip<title>. Screen readers ignore them; the adjacent text carries the meaning. - Meaningful icons. Add
role="img"plus a<title>child (or a visually-hidden sibling<span>). Assistive tech announces the title as the icon's accessible name. - Icon-only buttons. Put
aria-label="Close"on the<button>— not on the SVG. The button is the interactive element; it owns the label. - Touch targets. An icon rendered at 16–24px needs a minimum
44 × 44pxclick area around it. Pad the button, don't shrink the hit box — that is a WCAG 2.5.5 requirement and a finger-on-phone reality.
Third-party icon packs
css-is-awesome has no runtime dependency on a third-party pack — the shipped public/icons/*.svg are hand-authored. If your team already uses Heroicons, Lucide, Phosphor, or Feather, all four emit inline SVG with stroke="currentColor" and 24×24 viewBoxes, so they drop in with zero conversion. Import the React component the pack ships, or copy the raw SVG markup into your component and follow the conventions above. The SCSS layer also ships an opt-in Font Awesome integration (see m.fa-icon, m.fa-text, m.fa-spin in scss/_icons.scss) for teams already on a Font Awesome kit — it is disabled by default.
FA 6 Free is the default; flip $fa-pro: true in your theme config and fa-load emits Light / Thin / Duotone alongside Solid / Regular / Brands, with the family map switched to Font Awesome 6 Pro automatically.
// app.scss — turn Pro on once, fa-load + fa() both follow @use 'css-is-awesome/scss/theme/icons' with ($fa-pro: true); @use 'css-is-awesome/scss/main'; @use 'css-is-awesome/scss/icons' as i; :root { @include i.fa-load; } .note::before { @include i.fa(check, light); }
Drop the woff2 files for the weights you ship at $theme-fa-path (default /webfonts). Missing files don’t error — they just don’t render until you add them, so you can ramp up to the full Pro set incrementally.
Further reading
- Tokens → Palette — the color tokens
currentColorultimately inherits from. - Accessibility — labels, focus, and touch-target rules that apply beyond icons.
public/icons/README.md— the in-repo authoring reference covering the SVG mixin API, aliases, per-theme icon packs, and troubleshooting.