Icon ComponentThe Icon component provides a versatile, cross-platform solution for displaying icons in Summon applications. Itsupports multiple icon types including SVG vectors, font-based icons (Material Icons, Font Awesome), and imagefallbacks.
Navigation

Icon Component

The Icon component provides a versatile, cross-platform solution for displaying icons in Summon applications. It supports multiple icon types including SVG vectors, font-based icons (Material Icons, Font Awesome), and image fallbacks.

Overview

Icons are essential UI elements that provide visual cues and enhance user experience. The Summon Icon component offers:

  • Multiple Icon Types: SVG, font-based, and image icons
  • Accessibility First: Built-in ARIA labels and semantic roles
  • Interactive Support: Click handlers with proper accessibility
  • Type-Safe Styling: Full modifier system integration
  • Cross-Platform: Consistent rendering across browser and JVM environments

Basic Usage

Simple Icon

import codes.yousef.summon.components.display.Icon
import codes.yousef.summon.components.display.IconType

@Composable
fun SimpleIconExample() {
    Icon(
        name = "home",
        modifier = Modifier()
            .size("24px")
            .color("#333333")
    )
}

Material Design Icon

import codes.yousef.summon.components.display.MaterialIcon

@Composable
fun MaterialIconExample() {
    MaterialIcon(
        name = "favorite",
        size = "32px",
        color = "#FF5722",
        modifier = Modifier().padding("8px")
    )
}

SVG Icon

import codes.yousef.summon.components.display.SvgIcon

@Composable
fun SvgIconExample() {
    val heartSvg = """
        <svg viewBox="0 0 24 24">
            <path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/>
        </svg>
    """.trimIndent()

    SvgIcon(
        svgContent = heartSvg,
        ariaLabel = "Favorite",
        modifier = Modifier()
            .size("20px")
            .color("#E91E63")
    )
}

API Reference

Icon

The main icon component that supports all icon types.

@Composable
fun Icon(
    name: String,
    modifier: Modifier = Modifier(),
    size: String? = null,
    color: String? = null,
    type: IconType = IconType.SVG,
    fontFamily: String? = null,
    svgContent: String? = null,
    ariaLabel: String? = null,
    onClick: (() -> Unit)? = null
)

Parameters

Parameter Type Default Description
name String Required Icon identifier or ligature name
modifier Modifier Modifier() Styling and layout modifier
size String? null Icon size (prefer using modifier)
color String? null Icon color (prefer using modifier)
type IconType IconType.SVG Rendering strategy for the icon
fontFamily String? null Font family for font icons
svgContent String? null Raw SVG markup for SVG icons
ariaLabel String? null Accessible label for screen readers
onClick (() -> Unit)? null Click handler for interactive icons

IconType

Enum defining different icon rendering strategies.

enum class IconType {
    SVG,    // Vector SVG icons
    FONT,   // Font-based icons (Material Icons, Font Awesome)
    IMAGE   // Image-based icons (fallback)
}

MaterialIcon

Convenience component for Material Design icons.

@Composable
fun MaterialIcon(
    name: String,
    modifier: Modifier = Modifier(),
    size: String = IconDefaults.Size.MEDIUM,
    color: String? = null,
    onClick: (() -> Unit)? = null
)

Requires the Material Icons font to be loaded in your application.

FontAwesomeIcon

Convenience component for Font Awesome icons.

@Composable
fun FontAwesomeIcon(
    name: String,
    modifier: Modifier = Modifier(),
    size: String = IconDefaults.Size.MEDIUM,
    color: String? = null,
    fontFamily: String = "Font Awesome 5 Free",
    onClick: (() -> Unit)? = null
)

SvgIcon

Component for inline SVG icons.

@Composable
fun SvgIcon(
    svgContent: String,
    modifier: Modifier = Modifier(),
    size: String = IconDefaults.Size.MEDIUM,
    color: String? = null,
    ariaLabel: String? = null,
    onClick: (() -> Unit)? = null
)

IconDefaults

Predefined constants and common icons.

Size Presets

object Size {
    const val SMALL = "16px"
    const val MEDIUM = "24px"
    const val LARGE = "32px"
}

Common Icons

// Basic actions
IconDefaults.Add()
IconDefaults.Delete()
IconDefaults.Edit()
IconDefaults.Download()
IconDefaults.Upload()

// Status indicators
IconDefaults.Info()
IconDefaults.CheckCircle()
IconDefaults.Warning()
IconDefaults.Error()
IconDefaults.Close()

Advanced Examples

Interactive Icon Button

@Composable
fun IconButton(
    icon: String,
    label: String,
    onClick: () -> Unit,
    enabled: Boolean = true
) {
    MaterialIcon(
        name = icon,
        modifier = Modifier()
            .size("24px")
            .padding("8px")
            .borderRadius("4px")
            .backgroundColor(if (enabled) "#E3F2FD" else "#F5F5F5")
            .color(if (enabled) "#1976D2" else "#9E9E9E")
            .cursor(if (enabled) "pointer" else "not-allowed")
            .hover { backgroundColor("#BBDEFB") },
        onClick = if (enabled) onClick else null
    ).apply {
        if (!enabled) {
            ariaLabel("$label (disabled)")
        } else {
            ariaLabel(label)
        }
    }
}

Status Icon with Color Variants

@Composable
fun StatusIcon(
    status: Status,
    modifier: Modifier = Modifier()
) {
    val (iconName, color) = when (status) {
        Status.SUCCESS -> "check_circle" to "#4CAF50"
        Status.WARNING -> "warning" to "#FF9800"
        Status.ERROR -> "error" to "#F44336"
        Status.INFO -> "info" to "#2196F3"
    }

    MaterialIcon(
        name = iconName,
        color = color,
        modifier = modifier.size("20px"),
        ariaLabel = "Status: ${status.name.lowercase()}"
    )
}

Animated Loading Icon

@Composable
fun LoadingIcon(
    modifier: Modifier = Modifier()
) {
    val rotationAnimation = remember {
        keyframes<Float> {
            durationMillis = 1000
            0f at 0
            360f at 1000
        }
    }

    MaterialIcon(
        name = "refresh",
        modifier = modifier
            .size("24px")
            .color("#1976D2")
            .rotate(rotationAnimation)
            .animation(infinite = true),
        ariaLabel = "Loading..."
    )
}

Icon with Badge

@Composable
fun NotificationIcon(
    count: Int,
    modifier: Modifier = Modifier()
) {
    Box(modifier = modifier.position("relative")) {
        MaterialIcon(
            name = "notifications",
            size = "24px",
            ariaLabel = "Notifications"
        )

        if (count > 0) {
            Badge(
                text = if (count > 99) "99+" else count.toString(),
                modifier = Modifier()
                    .position("absolute")
                    .top("-4px")
                    .right("-4px")
                    .backgroundColor("#F44336")
                    .color("white")
                    .borderRadius("50%")
                    .minWidth("18px")
                    .height("18px")
                    .fontSize("12px")
                    .textAlign("center")
            )
        }
    }
}

Accessibility Guidelines

Screen Reader Support

Always provide meaningful labels for icons:

// Good - Descriptive label
Icon(
    name = "delete",
    ariaLabel = "Delete item",
    onClick = { deleteItem() }
)

// Bad - No label
Icon(
    name = "delete",
    onClick = { deleteItem() }
)

Interactive Icons

For clickable icons, ensure proper semantic roles:

// Automatically adds role="button" for interactive icons
Icon(
    name = "settings",
    ariaLabel = "Open settings",
    onClick = { openSettings() }
)

Color and Contrast

Ensure sufficient color contrast for icon visibility:

// High contrast for better accessibility
Icon(
    name = "warning",
    color = "#D32F2F", // Strong red for warnings
    ariaLabel = "Warning: Action required"
)

Performance Considerations

Icon Loading Strategies

  1. Font Icons: Load font files once, instant rendering
  2. SVG Icons: Inline for small sets, external files for large sets
  3. Image Icons: Use appropriate formats (WebP, SVG) and sizes

Optimization Tips

// Use appropriate sizes
Icon(
    name = "home",
    modifier = Modifier()
        .size("16px") // Small for inline text
        .size("24px") // Standard for buttons
        .size("32px") // Large for primary actions
)

// Prefer modifier styling over parameters
Icon(
    name = "star",
    modifier = Modifier()
        .size("20px")
        .color("#FFD700") // Better than color parameter
)

Platform-Specific Behavior

Browser Platform

  • Font icons require font files to be loaded
  • SVG icons support full CSS styling
  • Click events handled via DOM event listeners
  • Supports CSS animations and transitions

JVM Platform

  • Font icons rendered using system fonts or embedded resources
  • SVG icons converted to appropriate formats for output
  • Click events may require special handling depending on output format
  • Animation support varies by rendering target

Common Patterns

Icon Library Setup

// Create your icon library
object AppIcons {
    @Composable
    fun Home(modifier: Modifier = Modifier()) =
        MaterialIcon("home", modifier)

    @Composable
    fun User(modifier: Modifier = Modifier()) =
        MaterialIcon("person", modifier)

    @Composable
    fun Settings(modifier: Modifier = Modifier()) =
        MaterialIcon("settings", modifier)
}

// Usage
AppIcons.Home(
    modifier = Modifier()
        .size("24px")
        .color("#1976D2")
)

Responsive Icon Sizes

@Composable
fun ResponsiveIcon(
    name: String,
    modifier: Modifier = Modifier()
) {
    val screenSize = LocalBreakpoint.current
    val iconSize = when (screenSize) {
        Breakpoint.MOBILE -> "20px"
        Breakpoint.TABLET -> "24px"
        Breakpoint.DESKTOP -> "28px"
    }

    MaterialIcon(
        name = name,
        size = iconSize,
        modifier = modifier
    )
}

Testing

Unit Testing

@Test
fun testIconRendering() {
    val mockRenderer = MockPlatformRenderer()

    CompositionLocal.provideComposer(MockComposer()) {
        LocalPlatformRenderer.provides(mockRenderer) {
            Icon(
                name = "test-icon",
                ariaLabel = "Test Icon"
            )

            assertTrue(mockRenderer.renderIconCalled)
            assertEquals("test-icon", mockRenderer.lastIconNameRendered)
            assertEquals("Test Icon",
                mockRenderer.lastIconModifierRendered?.attributes?.get("aria-label"))
        }
    }
}

Accessibility Testing

@Test
fun testIconAccessibility() {
    val mockRenderer = MockPlatformRenderer()

    CompositionLocal.provideComposer(MockComposer()) {
        LocalPlatformRenderer.provides(mockRenderer) {
            Icon(
                name = "clickable",
                ariaLabel = "Clickable Icon",
                onClick = { }
            )

            val attributes = mockRenderer.lastIconModifierRendered?.attributes
            assertEquals("Clickable Icon", attributes?.get("aria-label"))
            assertEquals("button", attributes?.get("role"))
            assertEquals("pointer", mockRenderer.lastIconModifierRendered?.styles?.get("cursor"))
        }
    }
}

Migration Guide

From Raw HTML

<!-- Before: Raw HTML -->
<i class="material-icons" style="font-size: 24px; color: #1976D2;">home</i>

<!-- After: Summon Icon -->
MaterialIcon(
    name = "home",
    size = "24px",
    color = "#1976D2"
)

From Other UI Frameworks

// React Material-UI equivalent
// <HomeIcon fontSize="large" color="primary" />

MaterialIcon(
    name = "home",
    size = IconDefaults.Size.LARGE,
    color = theme.colors.primary
)

Best Practices

  1. Consistent Sizing: Use predefined size constants
  2. Meaningful Labels: Always provide accessible descriptions
  3. Performance: Choose appropriate icon type for your use case
  4. Theming: Use theme colors for consistent appearance
  5. Testing: Test with screen readers and keyboard navigation

The Icon component provides a robust foundation for icon usage across your Summon application, ensuring accessibility, performance, and cross-platform compatibility.

Architected in Kotlin. Rendered with Materia. Powered by Aether.
© 2026 Yousef.