import type { Meta, StoryObj } from '@storybook/web-components-vite'; import { html } from 'lit'; import './index.ts'; import type { USAIcon } from './usa-icon.js'; const meta: Meta = { title: 'Data Display/Icon', component: 'usa-icon', parameters: { layout: 'padded', docs: { description: { component: ` The USA Icon component provides a simple way to include icons in your application using the USWDS design system. Icons can be rendered either as inline SVGs or using an external sprite file for better performance with many icons. ## Features - Multiple size options (3-9) - Accessibility support with ARIA labels - Decorative icon support - Sprite-based or inline SVG rendering - Light DOM for USWDS CSS compatibility `, }, }, }, argTypes: { name: { control: 'select', options: [ 'search', 'close', 'menu', 'arrow_forward', 'arrow_back', 'arrow_upward', 'arrow_downward', 'check_circle', 'error', 'warning', 'info', 'help', 'flag', 'phone', 'mail', 'location_on', 'expand_more', 'expand_less', 'settings', 'file_download', 'cancel', ], description: 'Icon name to display', }, size: { control: 'select', options: ['', '3', '4', '5', '6', '7', '8', '9'], description: 'Icon size modifier', }, ariaLabel: { control: 'text', description: 'Accessible label for the icon', }, decorative: { control: 'boolean', description: 'Whether the icon is purely decorative', }, useSprite: { control: 'boolean', description: 'Use sprite file instead of inline SVG', }, spriteUrl: { control: 'text', description: 'URL to the SVG sprite file', }, }, }; export default meta; type Story = StoryObj; export const Default: Story = { args: { name: 'search', ariaLabel: 'Search', }, }; export const AllSizes: Story = { name: 'Icon Sizes', parameters: { controls: { disable: true }, docs: { description: { story: ` USWDS icons support multiple size variants from size 3 (smallest) to size 9 (largest). The default size (no size property) uses the standard icon dimensions. Click any card to copy the size value to your clipboard. `, }, }, }, render: () => html`
${[ { size: '', label: 'Default', class: 'usa-icon' }, { size: '3', label: 'Size 3', class: 'usa-icon--size-3' }, { size: '4', label: 'Size 4', class: 'usa-icon--size-4' }, { size: '5', label: 'Size 5', class: 'usa-icon--size-5' }, { size: '6', label: 'Size 6', class: 'usa-icon--size-6' }, { size: '7', label: 'Size 7', class: 'usa-icon--size-7' }, { size: '8', label: 'Size 8', class: 'usa-icon--size-8' }, { size: '9', label: 'Size 9', class: 'usa-icon--size-9' }, ].map((item) => html`
{ navigator.clipboard.writeText(item.size || 'default'); const feedback = document.createElement('div'); feedback.style.cssText = 'position: fixed; top: 20px; right: 20px; background: #00a91c; color: white; padding: 0.75rem 1rem; border-radius: 0.25rem; font-weight: 600; box-shadow: 0 4px 6px rgba(0,0,0,0.1); z-index: 9999;'; feedback.textContent = `Copied size="${item.size || ''}" to clipboard!`; document.body.appendChild(feedback); setTimeout(() => feedback.remove(), 2000); }} title="Click to copy size value" > ${item.label} ${item.class}
`)}
`, }; export const NavigationIcons: Story = { name: 'Navigation Icons', parameters: { controls: { disable: true }, docs: { description: { story: 'Common icons used for navigation and interface controls. Click any card to copy the icon name.', }, }, }, render: () => html`
${[ { name: 'menu', label: 'Menu' }, { name: 'close', label: 'Close' }, { name: 'search', label: 'Search' }, { name: 'settings', label: 'Settings' }, ].map((icon) => html`
{ navigator.clipboard.writeText(icon.name); const feedback = document.createElement('div'); feedback.style.cssText = 'position: fixed; top: 20px; right: 20px; background: #00a91c; color: white; padding: 0.75rem 1rem; border-radius: 0.25rem; font-weight: 600; box-shadow: 0 4px 6px rgba(0,0,0,0.1); z-index: 9999;'; feedback.textContent = `Copied "${icon.name}" to clipboard!`; document.body.appendChild(feedback); setTimeout(() => feedback.remove(), 2000); }} title="Click to copy icon name" > ${icon.label} ${icon.name}
`)}
`, }; export const ArrowIcons: Story = { name: 'Arrow Icons', parameters: { controls: { disable: true }, docs: { description: { story: 'Directional arrow icons for navigation and pagination. Click any card to copy the icon name.', }, }, }, render: () => html`
${[ { name: 'arrow_back', label: 'Back' }, { name: 'arrow_forward', label: 'Forward' }, { name: 'arrow_upward', label: 'Up' }, { name: 'arrow_downward', label: 'Down' }, ].map((icon) => html`
{ navigator.clipboard.writeText(icon.name); const feedback = document.createElement('div'); feedback.style.cssText = 'position: fixed; top: 20px; right: 20px; background: #00a91c; color: white; padding: 0.75rem 1rem; border-radius: 0.25rem; font-weight: 600; box-shadow: 0 4px 6px rgba(0,0,0,0.1); z-index: 9999;'; feedback.textContent = `Copied "${icon.name}" to clipboard!`; document.body.appendChild(feedback); setTimeout(() => feedback.remove(), 2000); }} title="Click to copy icon name" > ${icon.label} ${icon.name}
`)}
`, }; export const StatusIcons: Story = { name: 'Status & Alert Icons', parameters: { controls: { disable: true }, docs: { description: { story: 'Icons for displaying status, alerts, and feedback messages with appropriate semantic colors. Click any card to copy the icon name.', }, }, }, render: () => html`
${[ { name: 'check_circle', label: 'Success', colorClass: 'text-success', badge: 'text-success' }, { name: 'error', label: 'Error', colorClass: 'text-error', badge: 'text-error' }, { name: 'warning', label: 'Warning', colorClass: 'text-warning', badge: 'text-warning' }, { name: 'info', label: 'Info', colorClass: 'text-info-dark', badge: 'text-info-dark' }, ].map((icon) => html`
{ navigator.clipboard.writeText(icon.name); const feedback = document.createElement('div'); feedback.style.cssText = 'position: fixed; top: 20px; right: 20px; background: #00a91c; color: white; padding: 0.75rem 1rem; border-radius: 0.25rem; font-weight: 600; box-shadow: 0 4px 6px rgba(0,0,0,0.1); z-index: 9999;'; feedback.textContent = `Copied "${icon.name}" to clipboard!`; document.body.appendChild(feedback); setTimeout(() => feedback.remove(), 2000); }} title="Click to copy icon name" > ${icon.label} ${icon.name} ${icon.badge}
`)}
`, }; export const ContactIcons: Story = { name: 'Contact Icons', parameters: { controls: { disable: true }, docs: { description: { story: 'Icons for contact information and communication. Click any card to copy the icon name.', }, }, }, render: () => html`
${[ { name: 'phone', label: 'Phone' }, { name: 'mail', label: 'Email' }, { name: 'location_on', label: 'Location' }, ].map((icon) => html`
{ navigator.clipboard.writeText(icon.name); const feedback = document.createElement('div'); feedback.style.cssText = 'position: fixed; top: 20px; right: 20px; background: #00a91c; color: white; padding: 0.75rem 1rem; border-radius: 0.25rem; font-weight: 600; box-shadow: 0 4px 6px rgba(0,0,0,0.1); z-index: 9999;'; feedback.textContent = `Copied "${icon.name}" to clipboard!`; document.body.appendChild(feedback); setTimeout(() => feedback.remove(), 2000); }} title="Click to copy icon name" > ${icon.label} ${icon.name}
`)}
`, }; export const DecorativeIcon: Story = { args: { name: 'check_circle', size: '5', decorative: 'true', }, render: (args) => html`
Verified Organization
`, }; export const IconWithText: Story = { parameters: { controls: { disable: true }, // Static demo - no interactive controls needed }, render: () => html` `, }; export const ExpandCollapse: Story = { parameters: { controls: { disable: true }, // Static demo - no interactive controls needed }, render: () => html`
`, }; export const SpriteExample: Story = { name: 'Using Icon Sprites', parameters: { controls: { disable: true }, docs: { description: { story: ` For production applications with many icons, USWDS recommends using sprite files for better performance. The complete USWDS icon set (243 icons) is available via the official sprite file. This example shows how to configure the component for sprite usage. `, }, }, }, render: () => html`

Note: This is a documentation example. The actual sprite file would need to be included in your project from the USWDS package or CDN.

1. Install USWDS and locate the sprite file:
npm install @uswds/uswds

# Sprite file location:
node_modules/@uswds/uswds/dist/img/sprite.svg
2. Copy sprite to your public directory:
cp node_modules/@uswds/uswds/dist/img/sprite.svg public/img/
3. Use icons with sprite file:
<usa-icon
  name="search"
  size="5"
  aria-label="Search"
  useSprite
  spriteUrl="/img/sprite.svg"
></usa-icon>
4. Set global sprite URL (optional):
// Set once for all icons in your app
const icons = document.querySelectorAll('usa-icon');
icons.forEach(icon => {
  icon.useSprite = true;
  icon.spriteUrl = '/img/sprite.svg';
});
Benefits of using sprite files:
  • Performance: Single HTTP request for all icons
  • Caching: Browser caches the sprite file
  • Complete set: Access to all 243 USWDS icons
  • Consistency: Icons always match USWDS design

Best Practice: Use inline SVGs (current implementation) for a few icons, switch to sprites when using 10+ icons on a page for optimal performance.

`, }; export const OrganizationBanner: Story = { parameters: { controls: { disable: true }, // Static demo - no interactive controls needed }, render: () => html`
Verified organization website
Trusted and secure
`, }; // Icons with built-in SVG paths (inline rendering without sprite file) // These are the commonly used USWDS icons that work out-of-the-box const availableIcons = [ 'search', 'close', 'menu', 'arrow_forward', 'arrow_back', 'arrow_upward', 'arrow_downward', 'check_circle', 'error', 'warning', 'info', 'help', 'flag', 'phone', 'mail', 'location_on', 'expand_more', 'expand_less', 'settings', 'file_download', 'cancel' ]; // Complete list of all 241 USWDS icons available via sprite file const allUSWDSIcons = ['accessibility_new', 'accessible_forward', 'account_balance', 'account_box', 'account_circle', 'add', 'add_circle', 'add_circle_outline', 'alarm', 'alternate_email', 'announcement', 'api', 'arrow_back', 'arrow_downward', 'arrow_drop_down', 'arrow_drop_up', 'arrow_forward', 'arrow_upward', 'assessment', 'attach_file', 'attach_money', 'autorenew', 'backpack', 'bathtub', 'bedding', 'bookmark', 'bug_report', 'build', 'calendar_today', 'campaign', 'camping', 'cancel', 'chat', 'check', 'check_box_outline_blank', 'check_circle', 'check_circle_outline', 'checkroom', 'chevron_left', 'chevron_right', 'clean_hands', 'close', 'closed_caption', 'clothes', 'cloud', 'code', 'comment', 'connect_without_contact', 'construction', 'construction_worker', 'contact_page', 'content_copy', 'coronavirus', 'credit_card', 'deck', 'delete', 'device_thermostat', 'directions', 'directions_bike', 'directions_bus', 'directions_car', 'directions_walk', 'do_not_disturb', 'do_not_touch', 'drag_handle', 'eco', 'edit', 'electrical_services', 'emoji_events', 'error', 'error_outline', 'event', 'expand_less', 'expand_more', 'facebook', 'fast_forward', 'fast_rewind', 'favorite', 'favorite_border', 'file_download', 'file_present', 'file_upload', 'filter_alt', 'filter_list', 'fingerprint', 'first_page', 'flag', 'flickr', 'flight', 'flooding', 'folder', 'folder_open', 'format_quote', 'format_size', 'forum', 'github', 'grid_view', 'group_add', 'groups', 'hearing', 'help', 'help_outline', 'highlight_off', 'history', 'home', 'hospital', 'hotel', 'hourglass_empty', 'hurricane', 'identification', 'image', 'info', 'info_outline', 'insights', 'instagram', 'keyboard', 'label', 'language', 'last_page', 'launch', 'lightbulb', 'lightbulb_outline', 'link', 'link_off', 'list', 'local_cafe', 'local_fire_department', 'local_gas_station', 'local_grocery_store', 'local_hospital', 'local_laundry_service', 'local_library', 'local_offer', 'local_parking', 'local_pharmacy', 'local_police', 'local_taxi', 'location_city', 'location_on', 'lock', 'lock_open', 'lock_outline', 'login', 'logout', 'loop', 'mail', 'mail_outline', 'map', 'masks', 'medical_services', 'menu', 'military_tech', 'more_horiz', 'more_vert', 'my_location', 'navigate_before', 'navigate_far_before', 'navigate_far_next', 'navigate_next', 'near_me', 'notifications', 'notifications_active', 'notifications_none', 'notifications_off', 'park', 'people', 'person', 'pets', 'phone', 'photo_camera', 'print', 'priority_high', 'public', 'push_pin', 'radio_button_unchecked', 'rain', 'reduce_capacity', 'remove', 'report', 'restaurant', 'rss_feed', 'safety_divider', 'sanitizer', 'save_alt', 'schedule', 'school', 'science', 'search', 'security', 'send', 'sentiment_dissatisfied', 'sentiment_neutral', 'sentiment_satisfied', 'sentiment_satisfied_alt', 'sentiment_very_dissatisfied', 'settings', 'severe_weather', 'share', 'shield', 'shopping_basket', 'snow', 'soap', 'social_distance', 'sort_arrow', 'spellcheck', 'star', 'star_half', 'star_outline', 'store', 'support', 'support_agent', 'text_fields', 'thumb_down_alt', 'thumb_up_alt', 'timer', 'toggle_off', 'toggle_on', 'topic', 'tornado', 'translate', 'trending_down', 'trending_up', 'twitter', 'undo', 'unfold_less', 'unfold_more', 'update', 'upload_file', 'verified', 'verified_user', 'visibility', 'visibility_off', 'volume_off', 'warning', 'wash', 'wifi', 'work', 'youtube', 'zoom_in', 'zoom_out', 'zoom_out_map']; export const IconGallery: Story = { name: 'All USWDS Icons', parameters: { controls: { disable: true }, docs: { description: { story: ` Gallery of all ${allUSWDSIcons.length} USWDS icons from the official sprite file. Click on any icon name to copy it to your clipboard for easy use in your code. **Note:** This gallery displays all icons using the \`useSprite\` property with the USWDS sprite file. Only ${availableIcons.length} icons have built-in inline SVG fallbacks for offline/demo use. See the [USWDS Icon Documentation](https://designsystem.digital.gov/components/icon/) for complete details. `, }, }, }, render: () => html`

💡 Tip: Click any icon to copy its name to your clipboard.
Total USWDS icons: ${allUSWDSIcons.length} icons available via sprite file (shown below)
Built-in inline SVG fallbacks: ${availableIcons.length} common icons

All ${allUSWDSIcons.length} USWDS icons are displayed below using the useSprite property with the USWDS sprite file. See the USWDS Icon Documentation for complete details.

`, };