Skip to content

Add .icon--svg for inline SVG icon sets - #359

Merged
dallasbpeters merged 4 commits into
mainfrom
svg-icons
Sep 30, 2026
Merged

dallasbpeters merged 4 commits into
mainfrom
svg-icons

Conversation

@dallasbpeters

@dallasbpeters dallasbpeters commented Sep 29, 2026 •

Copy link
Copy Markdown
Member

Task

No ticket. Companion to RoleModel/rolemodel_rails#211, which makes the --custom icon builder render SVG sets.

Why?

Every Optics icon set is an icon font from a CDN today. Apps that use SVG icons (Almanac uses Hugeicons, which is also RoleModel's brand set) style them by hand. This adds one shared set of rules, so SVG icons get the same size, weight, and emphasis modifiers as font icons.

Optics stays CSS only. It ships no SVG files. Each app keeps its own.

What Changed

  • src/components/icon.css: new .icon--svg modifier, nested inside .icon (icon icon--svg icon--large).
    • The SVG fills the .icon box, so the size modifiers work unchanged.
    • Color comes from currentColor.
    • icon--weight-* sets the stroke width. The rule targets svg [stroke-width], because a CSS rule on the <svg> root cannot beat a stroke-width attribute on a child. With no weight modifier, the set keeps its own widths.
    • icon--low-emphasis lowers the opacity.
    • .icon keeps the public API: --_op-icon-stroke-width-{light,normal,semi-bold,bold} and --_op-icon-svg-opacity-low-emphasis sit with the other icon options, and the weight modifiers set --__op-icon-stroke-width, which .icon--svg reads.
  • Icon.stories.js: new Svg story with sizes, weights and low emphasis.
  • Icon.mdx: "SVG Icons" section with the story, and the new variables in the API list. Apps with only SVG icons can use optics+no_icons.css.
  • CHANGELOG: 2.5.0 entry.
  • Version bumped to 2.5.0 in package.json and the .storybook/assets example pages.

Quality Assurance

  • Have you tagged the PR with the correct labels?
  • Have you validated the changes?
    • Have you run linters? (yarn sanity-check)
    • Have you run prettier?
    • Have you tried building the css?
    • Have you tried building storybook?
  • Have you updated any usage of changed tokens? No tokens changed.
  • Did you add a component?
    • Have you added it to the dependency graph?
    • Have you added it to the docs?
  • Did you update a component?
    • Have you updated the dependency graph? No new dependencies. The new rules use no tokens.
    • Have you updated the docs?
  • Do you need to update the package version? 2.4.0 → 2.5.0, with a CHANGELOG entry for everything since 2.4.0.
    • Did you update the example pages in .storybook/assets?
  • Were any changes made to the top level optics.css file?
    • Were those changes reflected in the other top level files?

Questions for reviewers

  • Is opacity the right meaning for low emphasis on SVGs? Font icons use the GRAD axis, which SVGs do not have.
  • Are the default stroke widths (1 / 1.5 / 2 / 2.5) right for the sets we care about? Lucide and Tabler draw at 2, Hugeicons at 1.5.

Screenshots

SVG Icons section of the Icon docs page:

SCR-20260930-inph

@Jeremy-Walton Jeremy-Walton left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add screenshots of the documentation page?

Also can you add back the checklist into the PR? There is a reason that exists in the PR template. It tells you how to check everything

Main question is around the class and what the intended usage is. It's named as a modifier and indicated to be used as such but the css is not nested to enforce that.

Comment thread src/components/icon.css Outdated
Comment thread src/components/icon.css Outdated
Review asked whether .icon--svg was a block or a modifier: it was named as
a modifier but not nested to enforce it. It is now the .svg-icon block,
used with .icon the same way .material-symbols-outlined is. .icon keeps
the public API: the stroke-width variables sit with the other icon
options, and the weight modifiers set --__op-icon-stroke-width, which
.svg-icon reads.

Adds an Svg story showing sizes, weights and low emphasis, and shows it in
the SVG Icons docs section.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dallasbpeters dallasbpeters changed the title Add .icon--svg for inline SVG icon sets Add .icon-svg for inline SVG icon sets Sep 30, 2026
Comment thread src/components/icon.css Outdated
Comment thread src/stories/Components/Icon/Icon.mdx Outdated
Per review, SVG icons are a modifier of .icon rather than their own
block. .icon--svg is now nested in .icon, so it only applies together
with .icon. The docs point at the rolemodel_rails --custom builder.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dallasbpeters dallasbpeters changed the title Add .icon-svg for inline SVG icon sets Add .icon--svg for inline SVG icon sets Sep 30, 2026
dallasbpeters added a commit to RoleModel/rolemodel_rails that referenced this pull request Sep 30, 2026
Optics settled on a modifier nested inside .icon (RoleModel/optics#359).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dallasbpeters
dallasbpeters merged commit c964cfa into main Sep 30, 2026
4 checks passed
@dallasbpeters
dallasbpeters deleted the svg-icons branch September 30, 2026 17:17
dallasbpeters added a commit to RoleModel/rolemodel_rails that referenced this pull request Sep 30, 2026
* Add Hugeicons as an inline SVG icon library

`bin/rails g rolemodel:optics:icons --hugeicons` renders icons as inline SVG
read from files the app vendors, instead of an icon font.

- SvgIconBuilder: reads app/icons/<name>.svg once per process (re-reads while
  code reloading is on). Decorative by default (aria-hidden); hover_text: makes
  the span role="img" with an aria-label.
- HugeiconsIconBuilder: app/icons/hugeicons/<style>/<name>.svg; filled: and
  duotone: map to solid-rounded and duotone-rounded, style: picks any style.
- Rolemodel::Optics::Hugeicons: fetches SVGs from the Hugeicons API, rewrites
  colors to currentColor, and finds the icons views use.
- Generator adds lib/tasks/optics_icons.rake (optics:icons:vendor) and
  spec/icons_spec.rb, which fails when a used icon is not vendored.

Files live outside app/assets because Propshaft would serve them as
downloads, which the Hugeicons Pro license does not allow.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Load Rolemodel::Optics in the generated icon task and spec

In a new app, bin/rails optics:icons:vendor failed with
uninitialized constant Rolemodel::Optics, because only the icon helper
required it. The task now requires it, and a bad icon name aborts with
its message instead of a stack trace.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Simplify: use CustomIconBuilder for SVG icons instead of a new builder

Review pointed out CustomIconBuilder already inlines SVGs, so the new
SvgIconBuilder, HugeiconsIconBuilder, API client, rake task and spec
template are gone.

What CustomIconBuilder still needed:
- Find the file with Propshaft as well as Sprockets. It called
  assets_manifest, which only Sprockets defines, so it raised on Propshaft.
- Drop the reference to an undefined `options`, which raised whenever
  the SVG was found.
- Emit Optics' `svg-icon` block so size, weight and emphasis modifiers
  apply to the SVG.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Emit Optics' icon--svg modifier instead of the svg-icon block

Optics settled on a modifier nested inside .icon (RoleModel/optics#359).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Components Changes to a component

Development

Successfully merging this pull request may close these issues.

2 participants