Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions bulma-ui/src/skill-examples/Icons.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Skills → Examples → Icons: the bestax-icons skill's canonical example,
// rendered live. Shows the app-root ConfigProvider, meaningful-vs-decorative
// accessibility, sizes/variants/features, and multi-segment IconText.
import React from 'react';
import type { Meta, StoryObj } from '@storybook/react-vite';
import { ExampleMeta } from './ExampleMeta';
import { ConfigProvider, Icon, IconText, Box, Button, Title } from '../index';

const SKILL_HREF =
'https://github.com/allxsmith/bestax/tree/main/skills/bestax-icons/SKILL.md';

const meta: Meta = {
title: 'Skills/Icons',
tags: ['autodocs'],
parameters: { layout: 'padded' },
};
export default meta;
type Story = StoryObj;

function Meta_({ prompt }: { prompt: string }) {
return (
<ExampleMeta
skill="bestax-icons"
skillHref={SKILL_HREF}
model="Claude Fable 5"
date="2026-07-14"
prompt={prompt}
/>
);
}

export const IconUsage: Story = {
name: 'Icon usage & accessibility',
render: function IconUsageExample() {
return (
<Box>
<Meta_ prompt="Add icons to my app: a warning indicator, a starred label, a save button, a loading spinner, and a metro-to-airport route line. Make them accessible." />
{/* One library for the whole app — 'fa' | 'mdi' | 'ion' |
'material-icons' | 'material-symbols' (Ionicons is 'ion',
NOT 'ionicons'). */}
<ConfigProvider iconLibrary="fa">
<Title size="5">Meaningful icons carry their own label</Title>
<Icon
name="triangle-exclamation"
textColor="warning"
ariaLabel="Warning"
/>
<Icon name="circle-check" textColor="success" ariaLabel="Success" />

<Title size="5" mt="4">
Decorative icons hide from screen readers
</Title>
<IconText iconProps={{ name: 'star', 'aria-hidden': 'true' }}>
Starred
</IconText>
<Button color="primary" ml="3">
<Icon name="floppy-disk" aria-hidden="true" />
<span>Save</span>
</Button>

<Title size="5" mt="4">
Sizes, variants, features, color
</Title>
<Icon
name="rocket"
size="large"
features="fa-2x"
ariaLabel="Launch"
/>
<Icon name="bell" variant="regular" ariaLabel="Notifications" />
<Icon name="github" variant="brands" ariaLabel="GitHub" />
<Icon
name="spinner"
features="fa-spin"
textColor="info"
ariaLabel="Loading"
/>

<Title size="5" mt="4">
Multi-segment icon text
</Title>
<IconText
items={[
{
iconProps: { name: 'train', 'aria-hidden': 'true' },
text: 'Metro',
},
{
iconProps: { name: 'arrow-right', 'aria-hidden': 'true' },
text: 'Airport',
},
]}
/>
</ConfigProvider>
</Box>
);
},
};
1 change: 1 addition & 0 deletions create-bestax/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ automatically when the task matches:
- **bestax-form** — build forms with the bestax form components (no form library).
- **bestax-theming** — colors, branding, and dark mode via the \`Theme\` component (\`colorMode\`).
- **bestax-layout-scaffold** — scaffold full pages (app shell, landing, centered, card grid).
- **bestax-icons** — icons via \`Icon\`/\`IconText\`: library setup, name formats, variants, a11y.

Prefer the library's components and these skills over hand-written Bulma markup or custom CSS.

Expand Down
72 changes: 72 additions & 0 deletions docs/docs/skills/icons.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
---
title: Icons
sidebar_label: Icons
sidebar_position: 5
---

# Icons skill

The [`bestax-icons`](https://github.com/allxsmith/bestax/blob/main/skills/bestax-icons/SKILL.md)
skill teaches an agent to use icons in an app built with `@allxsmith/bestax-bulma`: the
`Icon`/`IconText` components, the five supported icon libraries and their setup, the
per-library name formats and variants, and decorative-vs-labeled accessibility.

## Install

```bash
npx skills add https://github.com/allxsmith/bestax --skill bestax-icons
```

## What it teaches

- **One library, set once** — `ConfigProvider iconLibrary` (`'fa' | 'mdi' | 'ion' |
'material-icons' | 'material-symbols'`), so no per-`Icon` `library` prop. Including the
classic trap: the Ionicons value is **`'ion'`, not `'ionicons'`**.
- **Per-library name formats** — the same glyph is `rocket` (Font Awesome),
`rocket-launch` (MDI), and `rocket_launch` (Material Icons/Symbols, which render the name
as a font-ligature text node). The wrong format is the #1 cause of blank icons.
- **Variants and features** — Font Awesome's `solid`/`regular`/`brands`, Material Icons'
`round` vs Material Symbols' `rounded`, Ionicons' `outline`/`sharp` name suffixes, and
glyph scaling via `features` (`'fa-2x'`, `'fa-spin'`).
- **Accessibility** — a descriptive `ariaLabel` for meaningful standalone icons;
`aria-hidden` for decorative icons that sit next to visible text.

## Meaningful vs decorative

```tsx live
<Block>
<Icon name="triangle-exclamation" textColor="warning" ariaLabel="Warning" />
<IconText iconProps={{ name: 'star', 'aria-hidden': 'true' }}>
Starred
</IconText>
</Block>
```

## Sizes, variants, features

```tsx live
<Block>
<Icon name="rocket" size="large" features="fa-2x" ariaLabel="Launch" />
<Icon name="bell" variant="regular" ariaLabel="Notifications" />
<Icon name="github" variant="brands" ariaLabel="GitHub" />
<Icon
name="spinner"
features="fa-spin"
textColor="info"
ariaLabel="Loading"
/>
</Block>
```

## Reference material the agent reads on demand

- [`references/icon-libraries.md`](https://github.com/allxsmith/bestax/blob/main/skills/bestax-icons/references/icon-libraries.md)
— per-library setup (npm package / CDN script), full name-format and variant tables, and
the blank-icon troubleshooting checklist.
- [`examples/icon-usage.tsx`](https://github.com/allxsmith/bestax/blob/main/skills/bestax-icons/examples/icon-usage.tsx)
— the canonical runnable example.

## Related

- [Icon API reference](../api/elements/icon.md) · [IconText API reference](../api/elements/icontext.md)
- [Configuration guide](../guides/features/configuration.md) — `ConfigProvider` in depth.
3 changes: 3 additions & 0 deletions docs/docs/skills/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ npx skills add https://github.com/allxsmith/bestax --skill bestax-custom-compone
npx skills add https://github.com/allxsmith/bestax --skill bestax-form
npx skills add https://github.com/allxsmith/bestax --skill bestax-theming
npx skills add https://github.com/allxsmith/bestax --skill bestax-layout-scaffold
npx skills add https://github.com/allxsmith/bestax --skill bestax-icons
```

Starting a new app? `pnpm create bestax@latest` offers to **preinstall these skills** into the
Expand All @@ -32,3 +33,5 @@ Then explore each skill — what it does, how to install it, and live examples:
`--bulma-*` variables with the `Theme` component.
- **[Layout scaffold](./layout-scaffold)** — go from "build me a dashboard / landing page / catalog"
to a complete responsive page using the layout archetypes.
- **[Icons](./icons)** — use `Icon`/`IconText` with any of the five supported icon libraries:
per-library setup, name formats, variants, and decorative-vs-labeled accessibility.
1 change: 1 addition & 0 deletions docs/sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const sidebars = {
'skills/form',
'skills/theming',
'skills/layout-scaffold',
'skills/icons',
],
},
],
Expand Down
8 changes: 8 additions & 0 deletions skills/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ directory the agent reads on demand.
| [`bestax-form`](./bestax-form/SKILL.md) | Building **forms** — Field/Control composition, the full input inventory, and the validate-it-yourself error pattern (there is no form library). |
| [`bestax-theming`](./bestax-theming/SKILL.md) | **Theming** — customize colors, branding, fonts/radius tokens, and dark mode by overriding Bulma's `--bulma-*` variables with the `Theme` component. |
| [`bestax-layout-scaffold`](./bestax-layout-scaffold/SKILL.md) | **Layout scaffolding** — turn a high-level request (dashboard, landing page, auth page, catalog) into a complete responsive page from named layout archetypes. |
| [`bestax-icons`](./bestax-icons/SKILL.md) | **Icons** — the `Icon`/`IconText` components and the five supported libraries (Font Awesome, MDI, Ionicons, Material Icons/Symbols): setup, name formats, variants, and accessibility. |

## Install

Expand All @@ -25,6 +26,7 @@ npx skills add https://github.com/allxsmith/bestax --skill bestax-custom-compone
npx skills add https://github.com/allxsmith/bestax --skill bestax-form
npx skills add https://github.com/allxsmith/bestax --skill bestax-theming
npx skills add https://github.com/allxsmith/bestax --skill bestax-layout-scaffold
npx skills add https://github.com/allxsmith/bestax --skill bestax-icons
```

## Layout
Expand Down Expand Up @@ -64,6 +66,12 @@ skills/
centered.tsx # centered single column
card-grid.tsx # multiline columns of cards
content-page.tsx # hero + cards + CTA, helper props (no inline style)
bestax-icons/
SKILL.md
references/
icon-libraries.md # per-library setup, name formats, variants, troubleshooting
examples/
icon-usage.tsx # ConfigProvider + sizes/variants/colors + a11y patterns
```

> `component-catalog.md` is generated from the API docs by `scripts/gen-component-catalog.mjs`
Expand Down
78 changes: 78 additions & 0 deletions skills/bestax-icons/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
---
name: bestax-icons
description: Use icons in an app built with @allxsmith/bestax-bulma — the Icon/IconText components and the five supported libraries (Font Awesome, Material Design Icons, Ionicons, Google Material Icons, Material Symbols). Use when adding an icon, choosing or configuring the app-wide icon library, fixing an icon that renders blank, pairing icons with text, or making icons accessible (decorative vs labeled).
license: MIT
---

# Icons with @allxsmith/bestax-bulma

`Icon` renders a Bulma icon container (`span.icon`) around a glyph from any of five icon
libraries behind one normalized API. `IconText` pairs icons with text. The library ships **no
icon fonts** — the chosen library's package (or CDN script) must be installed in the app.

## Quick start

```tsx
import { ConfigProvider, Icon, IconText } from '@allxsmith/bestax-bulma';

// Set the library ONCE at the app root; <Icon> then never needs `library`.
<ConfigProvider iconLibrary="fa">
<App />
</ConfigProvider>;

// Inside the app:
<Icon name="rocket" ariaLabel="Launch" />;
<IconText iconProps={{ name: 'star', 'aria-hidden': 'true' }}>
Starred
</IconText>;
```

## The five libraries

| Library | `iconLibrary` / `library` value | Name format | Example `name` |
| ---------------------- | ------------------------------- | ---------------------------- | --------------- |
| Font Awesome (default) | `'fa'` | kebab-case, no `fa-` prefix | `rocket` |
| Material Design Icons | `'mdi'` | kebab-case, no `mdi-` prefix | `rocket-launch` |
| Ionicons | `'ion'` | kebab-case | `rocket` |
| Google Material Icons | `'material-icons'` | snake_case (a text ligature) | `rocket_launch` |
| Material Symbols | `'material-symbols'` | snake_case (a text ligature) | `rocket_launch` |

⚠️ **The Ionicons value is `'ion'`, not `'ionicons'`.** The `npm create bestax` scaffold's
`--icon ionicons` flag maps to `iconLibrary="ion"` — passing `'ionicons'` to `ConfigProvider`
or `library` silently renders nothing.

**The same glyph has a different name per library** (`rocket` vs `rocket-launch` vs
`rocket_launch`). When an icon renders blank, the name format for the active library is the
first thing to check. A redundant `fa-`/`mdi-` prefix in `name` is tolerated (stripped), but
don't rely on it.

## Styling

- `size` — `'small' | 'medium' | 'large'` sizes the Bulma **container** (`is-small` ≈ 1rem,
`is-medium` ≈ 2rem, `is-large` ≈ 3rem box). To scale the **glyph**, use `features`
(Font Awesome `'fa-lg'`/`'fa-2x'`) or a Bulma text-size class (`'is-size-3'`).
- `variant` — per-library style: Font Awesome `solid` (default) / `regular` / `brands` /
`light` / `duotone` / `thin`; Material Icons `filled` (default) / `outlined` / `round` /
`sharp`; Material Symbols `outlined` (default) / `rounded` / `sharp`; Ionicons `outline` /
`sharp`. MDI has no variants. Note Material **Icons** uses `round`, Material **Symbols**
uses `rounded`.
- `features` — extra library classes, string or array: `'fa-spin'`, `['fa-lg', 'fa-border']`.
- Color via the helper props: `textColor="primary"`, `textColor="danger"`, etc.

## Accessibility

Every `Icon` renders `aria-label` (default `"icon"`).

- **Meaningful icon** (stands alone, conveys information): pass a descriptive
`ariaLabel="Delete item"`.
- **Decorative icon** (next to visible text that says the same thing, e.g. inside `IconText`
or a labeled `Button`): hide it from screen readers with `aria-hidden`:
`<Icon name="check" aria-hidden="true" />` — otherwise "icon" (or a duplicate label) is
announced alongside the text.

## References

- `references/icon-libraries.md` — per-library setup (install/import/CDN), the full
name-format and variant tables, `features` values, and the blank-icon troubleshooting list.
- `examples/icon-usage.tsx` — runnable example: ConfigProvider setup, sizes, variants,
colors, IconText, and decorative-vs-labeled patterns.
80 changes: 80 additions & 0 deletions skills/bestax-icons/examples/icon-usage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Icons with @allxsmith/bestax-bulma — the canonical patterns.
//
// The app must load the icon library itself (bestax ships no icon fonts):
// here Font Awesome, via `npm install @fortawesome/fontawesome-free` and
// `import '@fortawesome/fontawesome-free/css/all.min.css';` in main.tsx.
// Set the library ONCE on ConfigProvider ('fa' | 'mdi' | 'ion' |
// 'material-icons' | 'material-symbols' — Ionicons is 'ion', NOT 'ionicons');
// every <Icon> below then omits `library`.
import React from 'react';
import {
ConfigProvider,
Icon,
IconText,
Button,
Box,
Title,
} from '@allxsmith/bestax-bulma';

export function IconShowcase() {
return (
<ConfigProvider iconLibrary="fa">
<Box>
<Title size="5">Meaningful icons carry their own label</Title>
{/* Standalone icons convey information — give them a descriptive
ariaLabel (the default is just "icon"). */}
<Icon
name="triangle-exclamation"
textColor="warning"
ariaLabel="Warning"
/>
<Icon name="circle-check" textColor="success" ariaLabel="Success" />

<Title size="5" mt="4">
Decorative icons hide from screen readers
</Title>
{/* Next to visible text the icon repeats the message — aria-hidden
stops "icon" (or a duplicate) being announced. */}
<IconText iconProps={{ name: 'star', 'aria-hidden': 'true' }}>
Starred
</IconText>
<Button color="primary">
<Icon name="floppy-disk" aria-hidden="true" />
<span>Save</span>
</Button>

<Title size="5" mt="4">
Sizes, variants, features, color
</Title>
{/* `size` sizes the Bulma container; `features` scales the glyph. */}
<Icon name="rocket" size="large" features="fa-2x" ariaLabel="Launch" />
{/* Font Awesome styles come from `variant`; brands require it. */}
<Icon name="bell" variant="regular" ariaLabel="Notifications" />
<Icon name="github" variant="brands" ariaLabel="GitHub" />
{/* Spin + color via features and the textColor helper. */}
<Icon
name="spinner"
features="fa-spin"
textColor="info"
ariaLabel="Loading"
/>

<Title size="5" mt="4">
Multi-segment icon text
</Title>
<IconText
items={[
{
iconProps: { name: 'train', 'aria-hidden': 'true' },
text: 'Metro',
},
{
iconProps: { name: 'arrow-right', 'aria-hidden': 'true' },
text: 'Airport',
},
]}
/>
</Box>
</ConfigProvider>
);
}
Loading
Loading