home / skills / chunkytortoise / enterprisehub / web-artifacts-builder

This skill helps you build interactive web artifacts through live demos and component showcases for testing and demonstration.

npx playbooks add skill chunkytortoise/enterprisehub --skill web-artifacts-builder

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
47.1 KB
---
name: Web Artifacts Builder
description: This skill should be used when creating "interactive web components", "live demos", "prototype interfaces", "component showcases", "interactive examples", "web-based tools", or when building dynamic web artifacts for testing and demonstration purposes.
version: 1.0.0
---

# Web Artifacts Builder: Interactive Component Generation

## Overview

This skill provides tools and patterns for creating interactive web artifacts, including live component demos, prototype interfaces, and dynamic web-based tools. Particularly useful for showcasing Streamlit components and creating interactive examples.

## When to Use This Skill

Use this skill when creating:
- **Interactive component demos** and showcases
- **Prototype interfaces** for user testing
- **Live documentation** with working examples
- **Web-based tools** and utilities
- **A/B testing interfaces**
- **Component galleries** and design systems
- **Interactive tutorials** and onboarding flows

## Core Artifact Types

### 1. Interactive Component Showcase

```python
"""
Interactive showcase for demonstrating component variations
"""

import streamlit as st
from typing import Dict, Any, List, Optional, Callable
from dataclasses import dataclass, field
from enum import Enum
import json
import inspect
from pathlib import Path


class ArtifactType(Enum):
    """Types of web artifacts that can be built."""
    COMPONENT_SHOWCASE = "component_showcase"
    INTERACTIVE_DEMO = "interactive_demo"
    PROTOTYPE_INTERFACE = "prototype_interface"
    DESIGN_SYSTEM_GALLERY = "design_system_gallery"
    TUTORIAL_FLOW = "tutorial_flow"
    A_B_TEST_INTERFACE = "ab_test_interface"


@dataclass
class ComponentVariation:
    """Represents a variation of a component for showcase."""
    name: str
    description: str
    props: Dict[str, Any]
    code_example: Optional[str] = None
    notes: Optional[str] = None


@dataclass
class ArtifactConfiguration:
    """Configuration for building web artifacts."""
    title: str
    description: str
    artifact_type: ArtifactType
    components: List[str] = field(default_factory=list)
    variations: List[ComponentVariation] = field(default_factory=list)
    interactive_controls: Dict[str, Any] = field(default_factory=dict)
    code_display: bool = True
    live_preview: bool = True
    responsive_preview: bool = False


class ComponentShowcaseBuilder:
    """Builder for interactive component showcases."""

    def __init__(self, design_system, ui_components):
        self.design_system = design_system
        self.ui_components = ui_components
        self.current_config = None

    def create_showcase(self, config: ArtifactConfiguration):
        """Create an interactive component showcase."""
        self.current_config = config

        # Inject showcase-specific styles
        self._inject_showcase_styles()

        # Main showcase layout
        st.title(config.title)
        st.markdown(config.description)

        # Create main layout
        if config.code_display:
            preview_col, code_col = st.columns([2, 1])
        else:
            preview_col = st.container()
            code_col = None

        # Interactive controls sidebar
        self._render_interactive_controls()

        # Component variations
        with preview_col:
            self._render_component_variations()

        # Code display
        if code_col:
            with code_col:
                self._render_code_examples()

        # Responsive preview if enabled
        if config.responsive_preview:
            self._render_responsive_preview()

    def _inject_showcase_styles(self):
        """Inject showcase-specific styling."""
        showcase_css = f"""
        <style>
        .showcase-container {{
            background: {self.design_system.tokens.surface};
            border: 1px solid {self.design_system.tokens.border};
            border-radius: {self.design_system.tokens.radius_medium};
            padding: {self.design_system.tokens.spacing_lg};
            margin: {self.design_system.tokens.spacing_md} 0;
        }}

        .variation-preview {{
            background: {self.design_system.tokens.background};
            border: 1px solid {self.design_system.tokens.border_light};
            border-radius: {self.design_system.tokens.radius_small};
            padding: {self.design_system.tokens.spacing_md};
            margin: {self.design_system.tokens.spacing_sm} 0;
            min-height: 100px;
            display: flex;
            align-items: center;
            justify-content: center;
        }}

        .code-preview {{
            background: #f8f9fa;
            border: 1px solid {self.design_system.tokens.border};
            border-radius: {self.design_system.tokens.radius_small};
            padding: {self.design_system.tokens.spacing_md};
            font-family: 'Monaco', 'Consolas', monospace;
            font-size: 12px;
            overflow-x: auto;
        }}

        .variation-tabs {{
            display: flex;
            border-bottom: 1px solid {self.design_system.tokens.border};
            margin-bottom: {self.design_system.tokens.spacing_md};
        }}

        .variation-tab {{
            padding: {self.design_system.tokens.spacing_sm} {self.design_system.tokens.spacing_md};
            border: none;
            background: transparent;
            cursor: pointer;
            border-bottom: 2px solid transparent;
            transition: {self.design_system.tokens.transition_fast};
        }}

        .variation-tab.active {{
            border-bottom-color: {self.design_system.tokens.accent};
            color: {self.design_system.tokens.accent};
        }}

        .device-preview {{
            border: 3px solid #333;
            border-radius: 20px;
            padding: 20px;
            background: #000;
            margin: {self.design_system.tokens.spacing_md} 0;
        }}

        .device-preview.mobile {{
            width: 375px;
            height: 667px;
        }}

        .device-preview.tablet {{
            width: 768px;
            height: 1024px;
        }}

        .device-preview.desktop {{
            width: 100%;
            height: 600px;
        }}
        </style>
        """

        st.markdown(showcase_css, unsafe_allow_html=True)

    def _render_interactive_controls(self):
        """Render interactive controls in sidebar."""
        st.sidebar.markdown("### Interactive Controls")

        # Render configured controls
        for control_key, control_config in self.current_config.interactive_controls.items():
            control_type = control_config.get('type', 'text')
            control_label = control_config.get('label', control_key)
            default_value = control_config.get('default')

            if control_type == 'text':
                value = st.sidebar.text_input(control_label, value=default_value)
            elif control_type == 'number':
                min_val = control_config.get('min', 0)
                max_val = control_config.get('max', 100)
                value = st.sidebar.slider(control_label, min_val, max_val, default_value)
            elif control_type == 'select':
                options = control_config.get('options', [])
                value = st.sidebar.selectbox(control_label, options, index=0)
            elif control_type == 'checkbox':
                value = st.sidebar.checkbox(control_label, value=default_value)
            elif control_type == 'color':
                value = st.sidebar.color_picker(control_label, value=default_value)

            # Store in session state for component access
            st.session_state[f"control_{control_key}"] = value

    def _render_component_variations(self):
        """Render component variations with live preview."""
        st.markdown("### Component Variations")

        # Create tabs for variations
        if len(self.current_config.variations) > 1:
            tab_names = [var.name for var in self.current_config.variations]
            tabs = st.tabs(tab_names)

            for i, variation in enumerate(self.current_config.variations):
                with tabs[i]:
                    self._render_single_variation(variation)
        else:
            for variation in self.current_config.variations:
                self._render_single_variation(variation)

    def _render_single_variation(self, variation: ComponentVariation):
        """Render a single component variation."""
        st.markdown(f"**{variation.name}**")
        st.markdown(variation.description)

        # Live preview container
        st.markdown('<div class="variation-preview">', unsafe_allow_html=True)

        # Apply interactive control values to props
        modified_props = variation.props.copy()
        for key, value in st.session_state.items():
            if key.startswith("control_"):
                prop_key = key.replace("control_", "")
                if prop_key in modified_props:
                    modified_props[prop_key] = value

        # Render the component with modified props
        self._render_component_with_props(modified_props)

        st.markdown('</div>', unsafe_allow_html=True)

        # Show notes if available
        if variation.notes:
            st.info(variation.notes)

    def _render_component_with_props(self, props: Dict[str, Any]):
        """Render component based on props configuration."""
        # This would be customized based on your component library
        component_type = props.get('_type', 'button')

        if component_type == 'button':
            self.ui_components.button(
                label=props.get('label', 'Button'),
                variant=props.get('variant', 'PRIMARY'),
                size=props.get('size', 'MEDIUM'),
                disabled=props.get('disabled', False)
            )

        elif component_type == 'alert':
            self.ui_components.alert(
                message=props.get('message', 'Alert message'),
                alert_type=props.get('alert_type', 'INFO'),
                dismissible=props.get('dismissible', False)
            )

        elif component_type == 'metric_card':
            self.ui_components.metric_card(
                title=props.get('title', 'Metric'),
                value=props.get('value', 100),
                change=props.get('change'),
                change_type=props.get('change_type'),
                description=props.get('description')
            )

    def _render_code_examples(self):
        """Render code examples for current variations."""
        st.markdown("### Code Examples")

        for variation in self.current_config.variations:
            if variation.code_example:
                st.markdown(f"**{variation.name}**")
                st.code(variation.code_example, language='python')


class InteractiveDemoBuilder:
    """Builder for interactive demos and prototypes."""

    def __init__(self, design_system):
        self.design_system = design_system
        self.demo_state = {}

    def create_property_search_demo(self):
        """Create an interactive property search demo."""
        st.title("🏠 Property Search Demo")
        st.markdown("Interactive demo of the property search functionality")

        # Demo configuration
        with st.expander("Demo Configuration", expanded=False):
            col1, col2 = st.columns(2)

            with col1:
                max_price = st.slider("Max Budget", 100000, 2000000, 500000, 50000)
                bedrooms = st.selectbox("Bedrooms", [1, 2, 3, 4, 5], index=2)

            with col2:
                location = st.text_input("Location", value="Rancho Cucamonga, CA")
                property_type = st.selectbox("Property Type", ["House", "Condo", "Townhouse"])

        # Search results simulation
        st.markdown("### Search Results")

        # Generate mock properties based on search criteria
        mock_properties = self._generate_mock_properties(max_price, bedrooms, location)

        # Display properties in grid
        cols = st.columns(2)
        for i, property_data in enumerate(mock_properties):
            with cols[i % 2]:
                self._render_property_demo_card(property_data)

        # Search analytics
        st.markdown("### Search Analytics")
        col1, col2, col3 = st.columns(3)

        with col1:
            st.metric("Properties Found", len(mock_properties))
        with col2:
            avg_price = sum(p['price'] for p in mock_properties) / len(mock_properties)
            st.metric("Average Price", f"${avg_price:,.0f}")
        with col3:
            st.metric("Search Time", "0.3s")

    def create_lead_scoring_demo(self):
        """Create an interactive lead scoring demo."""
        st.title("🎯 Lead Scoring Demo")
        st.markdown("Interactive demo of AI-powered lead scoring")

        # Lead information input
        with st.expander("Lead Information", expanded=True):
            col1, col2 = st.columns(2)

            with col1:
                lead_name = st.text_input("Lead Name", value="John Smith")
                email = st.text_input("Email", value="[email protected]")
                phone = st.text_input("Phone", value="(555) 123-4567")

            with col2:
                budget = st.slider("Budget Range", 100000, 2000000, 750000, 50000)
                timeline = st.selectbox("Timeline", ["Immediately", "Within 3 months", "Within 6 months", "Just browsing"])
                property_type = st.selectbox("Interest", ["First-time buyer", "Upgrading", "Investment", "Downsizing"])

        # Calculate mock lead score
        score = self._calculate_mock_lead_score(budget, timeline, property_type)

        # Display lead score
        st.markdown("### Lead Score Analysis")

        col1, col2 = st.columns([1, 2])

        with col1:
            # Lead score indicator (would use real component)
            self._render_lead_score_indicator(score)

        with col2:
            # Score factors
            st.markdown("**Score Factors:**")
            factors = [
                ("Budget Alignment", 85, "High budget indicates serious buyer"),
                ("Timeline Urgency", 70, "Immediate timeline shows readiness"),
                ("Property Type Match", 90, "Interest aligns with available inventory"),
                ("Contact Information", 100, "Complete contact details provided"),
            ]

            for factor, factor_score, description in factors:
                st.metric(factor, f"{factor_score}/100", help=description)

    def _generate_mock_properties(self, max_price: int, bedrooms: int, location: str) -> List[Dict[str, Any]]:
        """Generate mock property data based on search criteria."""
        import random

        properties = []
        for i in range(6):
            # Generate price within range
            price = random.randint(int(max_price * 0.7), max_price)

            # Generate other attributes
            bathrooms = random.choice([1, 1.5, 2, 2.5, 3]) if bedrooms <= 3 else random.choice([2.5, 3, 3.5, 4])
            sqft = random.randint(bedrooms * 400, bedrooms * 800)

            properties.append({
                'id': i,
                'address': f"{random.randint(100, 9999)} {random.choice(['Oak', 'Pine', 'Maple', 'Cedar'])} St, {location}",
                'price': price,
                'bedrooms': bedrooms,
                'bathrooms': bathrooms,
                'square_feet': sqft,
                'status': random.choice(['available', 'pending', 'available', 'available']),  # Bias toward available
                'image_url': f"https://picsum.photos/400/300?random={i}"
            })

        return properties

    def _calculate_mock_lead_score(self, budget: int, timeline: str, property_type: str) -> float:
        """Calculate a mock lead score based on inputs."""
        base_score = 50

        # Budget factor
        if budget >= 500000:
            base_score += 25
        elif budget >= 300000:
            base_score += 15
        else:
            base_score += 5

        # Timeline factor
        timeline_scores = {
            "Immediately": 30,
            "Within 3 months": 20,
            "Within 6 months": 10,
            "Just browsing": 2
        }
        base_score += timeline_scores.get(timeline, 0)

        # Property type factor
        type_scores = {
            "First-time buyer": 15,
            "Upgrading": 20,
            "Investment": 10,
            "Downsizing": 25
        }
        base_score += type_scores.get(property_type, 0)

        return min(base_score, 100)

    def _render_property_demo_card(self, property_data: Dict[str, Any]):
        """Render a property card for the demo."""
        # Simplified property card for demo
        with st.container():
            st.image(property_data['image_url'], use_container_width=True)
            st.markdown(f"**${property_data['price']:,}**")
            st.markdown(f"{property_data['address']}")
            st.markdown(f"🛏️ {property_data['bedrooms']} bed • 🚿 {property_data['bathrooms']} bath • 📐 {property_data['square_feet']:,} sqft")

            if st.button("View Details", key=f"demo_view_{property_data['id']}"):
                st.success(f"Viewing details for {property_data['address']}")

    def _render_lead_score_indicator(self, score: float):
        """Render lead score indicator for demo."""
        # Simplified version of the real component
        if score >= 80:
            color = "#10b981"  # Green
            status = "Hot"
        elif score >= 60:
            color = "#f59e0b"  # Amber
            status = "Warm"
        else:
            color = "#ef4444"  # Red
            status = "Cold"

        st.markdown(f"""
        <div style="
            text-align: center;
            padding: 20px;
            border: 2px solid {color};
            border-radius: 10px;
            background: linear-gradient(135deg, rgba(255,255,255,0.1), rgba(255,255,255,0.05));
        ">
            <div style="
                font-size: 3em;
                font-weight: bold;
                color: {color};
            ">{score:.0f}</div>
            <div style="
                font-size: 1.2em;
                color: {color};
                font-weight: 600;
                margin-top: 10px;
            ">{status} Lead</div>
        </div>
        """, unsafe_allow_html=True)


class DesignSystemGallery:
    """Gallery showcasing design system components."""

    def __init__(self, design_system, ui_components):
        self.design_system = design_system
        self.ui_components = ui_components

    def create_design_gallery(self):
        """Create a comprehensive design system gallery."""
        st.title("🎨 Design System Gallery")
        st.markdown("Comprehensive showcase of all design system components")

        # Design system overview
        with st.expander("Design System Overview", expanded=False):
            self._render_design_tokens_overview()

        # Component categories
        gallery_sections = [
            ("Typography", self._render_typography_gallery),
            ("Colors", self._render_color_gallery),
            ("Buttons", self._render_button_gallery),
            ("Forms", self._render_form_gallery),
            ("Cards", self._render_card_gallery),
            ("Alerts", self._render_alert_gallery),
            ("Data Display", self._render_data_gallery),
            ("Layout", self._render_layout_gallery),
        ]

        tabs = st.tabs([section[0] for section in gallery_sections])

        for i, (_, render_func) in enumerate(gallery_sections):
            with tabs[i]:
                render_func()

    def _render_design_tokens_overview(self):
        """Render design tokens overview."""
        col1, col2 = st.columns(2)

        with col1:
            st.markdown("### Color Tokens")
            colors = [
                ("Primary", self.design_system.tokens.primary),
                ("Secondary", self.design_system.tokens.secondary),
                ("Accent", self.design_system.tokens.accent),
                ("Success", self.design_system.tokens.success),
                ("Warning", self.design_system.tokens.warning),
                ("Error", self.design_system.tokens.error),
            ]

            for name, color in colors:
                st.markdown(f"""
                <div style="
                    display: flex;
                    align-items: center;
                    margin: 8px 0;
                ">
                    <div style="
                        width: 30px;
                        height: 30px;
                        background: {color};
                        border-radius: 4px;
                        margin-right: 12px;
                        border: 1px solid #e5e7eb;
                    "></div>
                    <span>{name}: <code>{color}</code></span>
                </div>
                """, unsafe_allow_html=True)

        with col2:
            st.markdown("### Spacing Tokens")
            spacing = [
                ("XS", self.design_system.tokens.spacing_xs),
                ("SM", self.design_system.tokens.spacing_sm),
                ("MD", self.design_system.tokens.spacing_md),
                ("LG", self.design_system.tokens.spacing_lg),
                ("XL", self.design_system.tokens.spacing_xl),
            ]

            for name, size in spacing:
                st.markdown(f"""
                <div style="
                    display: flex;
                    align-items: center;
                    margin: 8px 0;
                ">
                    <div style="
                        width: {size};
                        height: 20px;
                        background: {self.design_system.tokens.accent};
                        margin-right: 12px;
                    "></div>
                    <span>{name}: <code>{size}</code></span>
                </div>
                """, unsafe_allow_html=True)

    def _render_typography_gallery(self):
        """Render typography gallery."""
        st.markdown("### Typography Examples")

        typography_examples = [
            ("Title", "design-title", "Main page titles and headers"),
            ("Subtitle", "design-subtitle", "Section headers and subheadings"),
            ("Body", "design-body", "Regular body text and paragraphs"),
            ("Caption", "design-caption", "Small text and captions"),
        ]

        for name, css_class, description in typography_examples:
            st.markdown(f'<div class="{css_class}">Sample {name} Text</div>', unsafe_allow_html=True)
            st.markdown(f"*{description}*")
            st.markdown("---")

    def _render_color_gallery(self):
        """Render color palette gallery."""
        st.markdown("### Color Palette")

        color_groups = [
            ("Brand Colors", [
                ("Primary", self.design_system.tokens.primary),
                ("Secondary", self.design_system.tokens.secondary),
                ("Accent", self.design_system.tokens.accent),
            ]),
            ("Status Colors", [
                ("Success", self.design_system.tokens.success),
                ("Warning", self.design_system.tokens.warning),
                ("Error", self.design_system.tokens.error),
                ("Info", self.design_system.tokens.info),
            ]),
            ("Neutral Colors", [
                ("Background", self.design_system.tokens.background),
                ("Surface", self.design_system.tokens.surface),
                ("Text Primary", self.design_system.tokens.text_primary),
                ("Text Secondary", self.design_system.tokens.text_secondary),
                ("Text Muted", self.design_system.tokens.text_muted),
                ("Border", self.design_system.tokens.border),
            ])
        ]

        for group_name, colors in color_groups:
            st.markdown(f"#### {group_name}")
            cols = st.columns(len(colors))

            for i, (name, color) in enumerate(colors):
                with cols[i]:
                    st.markdown(f"""
                    <div style="
                        background: {color};
                        height: 80px;
                        border-radius: 8px;
                        border: 1px solid #e5e7eb;
                        margin-bottom: 8px;
                    "></div>
                    <div style="text-align: center;">
                        <strong>{name}</strong><br>
                        <code>{color}</code>
                    </div>
                    """, unsafe_allow_html=True)

    def _render_button_gallery(self):
        """Render button variations gallery."""
        st.markdown("### Button Variations")

        # Button variants
        variants = ["PRIMARY", "SECONDARY", "SUCCESS", "WARNING", "ERROR", "GHOST"]
        sizes = ["SMALL", "MEDIUM", "LARGE"]

        st.markdown("#### By Variant")
        cols = st.columns(len(variants))
        for i, variant in enumerate(variants):
            with cols[i]:
                st.button(f"{variant.title()} Button", key=f"btn_variant_{variant}")

        st.markdown("#### By Size")
        cols = st.columns(len(sizes))
        for i, size in enumerate(sizes):
            with cols[i]:
                st.button(f"{size.title()} Button", key=f"btn_size_{size}")

    def _render_form_gallery(self):
        """Render form components gallery."""
        st.markdown("### Form Components")

        col1, col2 = st.columns(2)

        with col1:
            st.text_input("Text Input", placeholder="Enter text here")
            st.number_input("Number Input", value=100)
            st.selectbox("Select Box", ["Option 1", "Option 2", "Option 3"])

        with col2:
            st.text_area("Text Area", placeholder="Enter longer text here")
            st.checkbox("Checkbox Option")
            st.radio("Radio Options", ["Option A", "Option B", "Option C"])

    def _render_card_gallery(self):
        """Render card components gallery."""
        st.markdown("### Card Components")

        col1, col2 = st.columns(2)

        with col1:
            self.ui_components.card(
                title="Basic Card",
                content="This is a basic card component with title and content."
            )

        with col2:
            self.ui_components.card(
                title="Card with Actions",
                content="This card includes action buttons.",
                actions=[
                    {"type": "button", "label": "Primary Action", "key": "card_action_1"},
                    {"type": "button", "label": "Secondary", "key": "card_action_2"}
                ]
            )

    def _render_alert_gallery(self):
        """Render alert components gallery."""
        st.markdown("### Alert Components")

        alert_types = ["INFO", "SUCCESS", "WARNING", "ERROR"]
        messages = {
            "INFO": "This is an informational alert.",
            "SUCCESS": "This is a success alert.",
            "WARNING": "This is a warning alert.",
            "ERROR": "This is an error alert."
        }

        for alert_type in alert_types:
            self.ui_components.alert(messages[alert_type], getattr(st, alert_type.lower(), st.info))

    def _render_data_gallery(self):
        """Render data display components gallery."""
        st.markdown("### Data Display Components")

        # Metrics
        col1, col2, col3 = st.columns(3)
        with col1:
            self.ui_components.metric_card("Total Sales", "$125,430", "+12%", "positive")
        with col2:
            self.ui_components.metric_card("New Leads", "245", "-5%", "negative")
        with col3:
            self.ui_components.metric_card("Conversion Rate", "18.5%", "+2.3%", "positive")

        # Progress bar
        st.markdown("#### Progress Indicators")
        self.ui_components.progress_bar(75, label="Project Progress", show_percentage=True)

        # Data table
        st.markdown("#### Data Table")
        sample_data = [
            {"Name": "John Doe", "Email": "[email protected]", "Score": 85},
            {"Name": "Jane Smith", "Email": "[email protected]", "Score": 92},
            {"Name": "Bob Johnson", "Email": "[email protected]", "Score": 78},
        ]
        self.ui_components.data_table(sample_data)

    def _render_layout_gallery(self):
        """Render layout components gallery."""
        st.markdown("### Layout Components")

        st.markdown("#### Two Column Layout")
        col1, col2 = st.columns(2)
        with col1:
            st.markdown("Left column content")
        with col2:
            st.markdown("Right column content")

        st.markdown("#### Three Column Layout")
        col1, col2, col3 = st.columns(3)
        with col1:
            st.markdown("Column 1")
        with col2:
            st.markdown("Column 2")
        with col3:
            st.markdown("Column 3")


# Usage Examples
def create_component_showcase_example():
    """Example of creating a component showcase."""
    from design.frontend_design import DesignSystem, UIComponents

    design_system = DesignSystem()
    ui_components = UIComponents(design_system)
    showcase_builder = ComponentShowcaseBuilder(design_system, ui_components)

    # Configure showcase
    config = ArtifactConfiguration(
        title="Button Component Showcase",
        description="Interactive showcase of button component variations",
        artifact_type=ArtifactType.COMPONENT_SHOWCASE,
        interactive_controls={
            'label': {
                'type': 'text',
                'label': 'Button Label',
                'default': 'Click Me'
            },
            'disabled': {
                'type': 'checkbox',
                'label': 'Disabled',
                'default': False
            }
        },
        variations=[
            ComponentVariation(
                name="Primary Button",
                description="Main action button with primary styling",
                props={'_type': 'button', 'variant': 'PRIMARY', 'label': 'Primary'},
                code_example='ui_components.button("Primary", variant=ButtonVariant.PRIMARY)'
            ),
            ComponentVariation(
                name="Secondary Button",
                description="Secondary action button with outline styling",
                props={'_type': 'button', 'variant': 'SECONDARY', 'label': 'Secondary'},
                code_example='ui_components.button("Secondary", variant=ButtonVariant.SECONDARY)'
            )
        ],
        code_display=True,
        live_preview=True,
        responsive_preview=True
    )

    # Create showcase
    showcase_builder.create_showcase(config)

def create_interactive_demo_example():
    """Example of creating an interactive demo."""
    design_system = DesignSystem()
    demo_builder = InteractiveDemoBuilder(design_system)

    # Create property search demo
    demo_builder.create_property_search_demo()

def create_design_gallery_example():
    """Example of creating a design system gallery."""
    from design.frontend_design import DesignSystem, UIComponents

    design_system = DesignSystem()
    ui_components = UIComponents(design_system)
    gallery = DesignSystemGallery(design_system, ui_components)

    # Create gallery
    gallery.create_design_gallery()
```

## Project-Specific Artifacts for EnterpriseHub

### Real Estate Demo Artifacts

```python
"""
Real estate specific web artifacts and demos
"""

class RealEstateArtifactBuilder:
    """Builder for real estate specific web artifacts."""

    def __init__(self, design_system):
        self.design_system = design_system

    def create_property_matching_demo(self):
        """Create interactive property matching demo."""
        st.title("🔍 AI Property Matching Demo")
        st.markdown("See how our AI matches properties to buyer preferences")

        # Buyer persona setup
        with st.expander("Buyer Persona", expanded=True):
            col1, col2, col3 = st.columns(3)

            with col1:
                buyer_name = st.text_input("Buyer Name", "Sarah Johnson")
                family_size = st.selectbox("Family Size", [1, 2, 3, 4, 5], index=2)
                lifestyle = st.selectbox("Lifestyle", ["Urban Professional", "Growing Family", "Empty Nester", "First-time Buyer"])

            with col2:
                budget_min = st.number_input("Min Budget", 200000, 2000000, 400000, 50000)
                budget_max = st.number_input("Max Budget", 300000, 3000000, 600000, 50000)
                preferred_areas = st.multiselect("Preferred Areas", ["Downtown", "Suburbs", "Waterfront", "Hills"])

            with col3:
                must_haves = st.multiselect("Must Haves", ["Pool", "Garage", "Garden", "Modern Kitchen", "Home Office"])
                nice_to_haves = st.multiselect("Nice to Haves", ["Fireplace", "Balcony", "Walk-in Closet", "Guest Room"])

        # AI Matching Process Visualization
        st.markdown("### AI Matching Process")

        if st.button("Run AI Matching"):
            # Simulate AI matching process
            with st.spinner("AI analyzing buyer preferences..."):
                import time
                time.sleep(1)

            with st.spinner("Searching property database..."):
                time.sleep(1)

            with st.spinner("Calculating compatibility scores..."):
                time.sleep(1)

            # Show results
            st.success("Matching complete! Found 8 compatible properties.")

            # Display matching results
            self._display_matching_results(buyer_name, budget_max, preferred_areas, must_haves)

    def create_ghl_workflow_demo(self):
        """Create GHL automation workflow demo."""
        st.title("⚡ GHL Automation Workflow Demo")
        st.markdown("Interactive demonstration of lead nurturing automation")

        # Workflow configuration
        workflow_steps = [
            {"name": "Lead Capture", "status": "completed"},
            {"name": "Initial Contact", "status": "completed"},
            {"name": "Lead Qualification", "status": "active"},
            {"name": "Property Matching", "status": "pending"},
            {"name": "Showing Scheduled", "status": "pending"},
            {"name": "Follow-up Sequence", "status": "pending"}
        ]

        # Create workflow visualization
        st.markdown("### Current Workflow Status")
        self._render_workflow_tracker(workflow_steps)

        # Lead information
        col1, col2 = st.columns(2)

        with col1:
            st.markdown("#### Lead Information")
            lead_data = {
                "Name": "Michael Chen",
                "Email": "[email protected]",
                "Phone": "(555) 987-6543",
                "Source": "Website Form",
                "Interest": "Investment Property",
                "Budget": "$500K - $750K"
            }

            for key, value in lead_data.items():
                st.write(f"**{key}:** {value}")

        with col2:
            st.markdown("#### Automation Actions")

            # Simulate automation actions
            if st.button("Send Welcome Email"):
                st.success("✅ Welcome email sent automatically")

            if st.button("Schedule Follow-up Call"):
                st.success("✅ Follow-up call scheduled for tomorrow 2 PM")

            if st.button("Trigger Property Matching"):
                st.success("✅ AI property matching initiated")

        # Activity Timeline
        st.markdown("### Activity Timeline")
        activities = [
            {"time": "2 minutes ago", "action": "Form submitted", "details": "Lead filled out contact form on website"},
            {"time": "1 minute ago", "action": "Welcome email sent", "details": "Automatic welcome email delivered"},
            {"time": "30 seconds ago", "action": "Lead scored", "details": "AI assigned lead score of 85/100"},
            {"time": "Just now", "action": "Property matching started", "details": "AI analyzing preferences for property matches"}
        ]

        for activity in activities:
            st.markdown(f"""
            <div style="
                padding: 12px;
                border-left: 3px solid {self.design_system.tokens.accent};
                background: {self.design_system.tokens.surface};
                margin: 8px 0;
                border-radius: 0 8px 8px 0;
            ">
                <div style="font-weight: 600; color: {self.design_system.tokens.text_primary};">
                    {activity['action']} <span style="color: {self.design_system.tokens.text_muted}; font-size: 0.9em;">({activity['time']})</span>
                </div>
                <div style="color: {self.design_system.tokens.text_secondary}; margin-top: 4px;">
                    {activity['details']}
                </div>
            </div>
            """, unsafe_allow_html=True)

    def create_ai_insights_demo(self):
        """Create AI insights and analytics demo."""
        st.title("🤖 AI Insights Demo")
        st.markdown("Real-time AI-powered insights and recommendations")

        # Market insights
        st.markdown("### Market Insights")

        col1, col2, col3 = st.columns(3)

        with col1:
            st.metric("Market Trend", "↗️ Rising", "5.2% vs last month")
        with col2:
            st.metric("Avg. Days on Market", "28 days", "-3 days vs last month")
        with col3:
            st.metric("Price per Sq Ft", "$285", "+$12 vs last month")

        # AI Recommendations
        st.markdown("### AI Recommendations")

        recommendations = [
            {
                "type": "opportunity",
                "title": "Pricing Opportunity Detected",
                "message": "Property at 123 Oak St is priced 8% below market value. Consider immediate showing.",
                "confidence": 0.92,
                "action": "Schedule Viewing"
            },
            {
                "type": "warning",
                "title": "Market Shift Alert",
                "message": "Interest rates expected to rise next month. Advise pre-approval acceleration.",
                "confidence": 0.85,
                "action": "Contact Lender"
            },
            {
                "type": "recommendation",
                "title": "Lead Nurturing Suggestion",
                "message": "3 leads showing similar patterns to recent conversions. Increase contact frequency.",
                "confidence": 0.78,
                "action": "Update Campaign"
            }
        ]

        for rec in recommendations:
            self._render_ai_recommendation(rec)

        # Predictive Analytics
        st.markdown("### Predictive Analytics")

        # Lead conversion prediction
        st.markdown("#### Lead Conversion Predictions")
        lead_predictions = [
            {"name": "Sarah Johnson", "score": 92, "conversion_probability": 85, "expected_close": "2-3 weeks"},
            {"name": "David Kim", "score": 78, "conversion_probability": 65, "expected_close": "1-2 months"},
            {"name": "Maria Rodriguez", "score": 65, "conversion_probability": 45, "expected_close": "2-3 months"},
        ]

        for lead in lead_predictions:
            self._render_lead_prediction(lead)

    def _display_matching_results(self, buyer_name: str, budget: int, areas: List[str], must_haves: List[str]):
        """Display AI property matching results."""
        st.markdown("### Matching Results")

        # Generate mock properties with compatibility scores
        mock_properties = [
            {
                "address": "456 Elm Street",
                "price": 485000,
                "bedrooms": 3,
                "bathrooms": 2,
                "compatibility": 96,
                "highlights": ["Modern Kitchen", "Garage", "Garden"],
                "concerns": []
            },
            {
                "address": "789 Pine Avenue",
                "price": 525000,
                "bedrooms": 4,
                "bathrooms": 3,
                "compatibility": 89,
                "highlights": ["Pool", "Home Office", "Modern Kitchen"],
                "concerns": ["Slightly over budget"]
            },
            {
                "address": "321 Oak Drive",
                "price": 445000,
                "bedrooms": 3,
                "bathrooms": 2,
                "compatibility": 82,
                "highlights": ["Garden", "Garage", "Fireplace"],
                "concerns": ["No pool"]
            }
        ]

        for prop in mock_properties:
            self._render_property_match_card(prop)

    def _render_property_match_card(self, property_data: Dict[str, Any]):
        """Render a property match card with compatibility score."""
        compatibility = property_data['compatibility']

        # Determine compatibility color
        if compatibility >= 90:
            compat_color = self.design_system.tokens.success
        elif compatibility >= 75:
            compat_color = self.design_system.tokens.warning
        else:
            compat_color = self.design_system.tokens.error

        st.markdown(f"""
        <div style="
            border: 2px solid {compat_color};
            border-radius: {self.design_system.tokens.radius_medium};
            padding: {self.design_system.tokens.spacing_md};
            margin: {self.design_system.tokens.spacing_sm} 0;
            background: {self.design_system.tokens.surface};
        ">
            <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
                <h4 style="margin: 0; color: {self.design_system.tokens.text_primary};">{property_data['address']}</h4>
                <div style="
                    background: {compat_color};
                    color: white;
                    padding: 4px 12px;
                    border-radius: 20px;
                    font-weight: bold;
                ">
                    {compatibility}% Match
                </div>
            </div>

            <div style="margin-bottom: 12px; color: {self.design_system.tokens.text_secondary};">
                <strong>${property_data['price']:,}</strong> • {property_data['bedrooms']} bed • {property_data['bathrooms']} bath
            </div>

            <div style="display: flex; gap: 20px;">
                <div style="flex: 1;">
                    <strong style="color: {self.design_system.tokens.success};">Highlights:</strong>
                    <ul style="margin: 4px 0 0 0; padding-left: 20px;">
                        {' '.join([f'<li>{highlight}</li>' for highlight in property_data['highlights']])}
                    </ul>
                </div>
                {f'<div style="flex: 1;"><strong style="color: {self.design_system.tokens.warning};">Considerations:</strong><ul style="margin: 4px 0 0 0; padding-left: 20px;">{" ".join([f"<li>{concern}</li>" for concern in property_data["concerns"]])}</ul></div>' if property_data['concerns'] else ''}
            </div>
        </div>
        """, unsafe_allow_html=True)

        # Action buttons
        col1, col2, col3 = st.columns(3)
        with col1:
            if st.button(f"Schedule Tour", key=f"tour_{hash(property_data['address'])}"):
                st.success(f"Tour scheduled for {property_data['address']}")
        with col2:
            if st.button(f"Get Details", key=f"details_{hash(property_data['address'])}"):
                st.info(f"Detailed information for {property_data['address']}")
        with col3:
            if st.button(f"Save Property", key=f"save_{hash(property_data['address'])}"):
                st.success(f"Property saved to favorites")

    def _render_workflow_tracker(self, steps: List[Dict[str, Any]]):
        """Render workflow progress tracker."""
        # Implementation would be similar to the workflow status tracker
        # from the frontend-design skill, but customized for GHL workflows
        pass

    def _render_ai_recommendation(self, recommendation: Dict[str, Any]):
        """Render AI recommendation card."""
        rec_type = recommendation['type']

        # Map recommendation types to colors
        type_colors = {
            'opportunity': self.design_system.tokens.success,
            'warning': self.design_system.tokens.warning,
            'recommendation': self.design_system.tokens.info
        }

        color = type_colors.get(rec_type, self.design_system.tokens.info)
        confidence = recommendation['confidence']

        st.markdown(f"""
        <div style="
            border: 1px solid {color};
            border-left: 4px solid {color};
            border-radius: {self.design_system.tokens.radius_small};
            padding: {self.design_system.tokens.spacing_md};
            margin: {self.design_system.tokens.spacing_sm} 0;
            background: rgba({color[1:3]}, {color[3:5]}, {color[5:7]}, 0.05);
        ">
            <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
                <h5 style="margin: 0; color: {color};">{recommendation['title']}</h5>
                <span style="font-size: 0.9em; color: {self.design_system.tokens.text_muted};">
                    Confidence: {confidence:.0%}
                </span>
            </div>
            <p style="margin: 8px 0; color: {self.design_system.tokens.text_primary};">
                {recommendation['message']}
            </p>
        </div>
        """, unsafe_allow_html=True)

        if st.button(recommendation['action'], key=f"rec_action_{hash(recommendation['title'])}"):
            st.success(f"Action '{recommendation['action']}' triggered")

    def _render_lead_prediction(self, lead_data: Dict[str, Any]):
        """Render lead conversion prediction."""
        probability = lead_data['conversion_probability']

        # Determine probability color
        if probability >= 80:
            prob_color = self.design_system.tokens.success
        elif probability >= 60:
            prob_color = self.design_system.tokens.warning
        else:
            prob_color = self.design_system.tokens.error

        st.markdown(f"""
        <div style="
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: {self.design_system.tokens.spacing_sm} {self.design_system.tokens.spacing_md};
            border: 1px solid {self.design_system.tokens.border};
            border-radius: {self.design_system.tokens.radius_small};
            margin: {self.design_system.tokens.spacing_xs} 0;
            background: {self.design_system.tokens.surface};
        ">
            <div>
                <strong>{lead_data['name']}</strong> (Score: {lead_data['score']})
            </div>
            <div style="text-align: right;">
                <div style="color: {prob_color}; font-weight: bold;">
                    {probability}% likely to convert
                </div>
                <div style="font-size: 0.9em; color: {self.design_system.tokens.text_muted};">
                    Expected: {lead_data['expected_close']}
                </div>
            </div>
        </div>
        """, unsafe_allow_html=True)
```

## Best Practices

1. **Interactive Controls**: Provide meaningful controls that demonstrate component flexibility
2. **Live Preview**: Show real-time updates as users modify controls
3. **Code Examples**: Include copy-pasteable code examples
4. **Responsive Testing**: Test artifacts across different screen sizes
5. **Performance**: Optimize for fast loading and smooth interactions
6. **Accessibility**: Ensure artifacts work with screen readers and keyboards
7. **Documentation**: Include clear descriptions and usage notes

This web artifacts builder skill provides comprehensive tools for creating interactive demos, component showcases, and prototype interfaces that effectively demonstrate the capabilities of the EnterpriseHub GHL Real Estate AI system.

Overview

This skill builds interactive web artifacts for demos, prototypes, component showcases, and web-based tools. It focuses on live previews, configurable component variations, and quick-demo flows that work well with Streamlit and Python UI libraries. Use it to accelerate testing, documentation, and stakeholder demos with minimal wiring.

How this skill works

The skill provides builders that render interactive showcases and demos by composing component variations, injecting styling, and exposing sidebar controls to mutate props at runtime. It maps control values into component props, renders live previews, and optionally shows runnable code examples. It also includes demo patterns (search, lead scoring) for domain examples like real-estate and CRM flows.

When to use it

  • Creating interactive component demos or design-system galleries
  • Building prototype interfaces for user testing or stakeholder feedback
  • Generating live documentation with runnable examples and code snippets
  • Constructing web-based tools, A/B test interfaces, or onboarding flows
  • Rapidly mocking domain-specific demos (e.g., property search, lead scoring)

Best practices

  • Define a clear ArtifactConfiguration for each showcase to centralize title, description, controls, and variations
  • Keep component props declarative and expose only meaningful controls to avoid UI clutter
  • Provide code_example snippets for each variation so engineers can copy runnable samples
  • Use responsive_preview and device presets when demonstrating cross-device behavior
  • Store interactive control values in session state so previews remain consistent during exploration

Example use cases

  • Component gallery for a design system showing button, alert, and card variations with live controls
  • Streamlit-based property search demo that generates mock listings and analytics for product reviews
  • Lead scoring interactive prototype to validate AI-driven heuristics with stakeholders
  • A/B test interface to compare two UI variants side-by-side with configurable inputs
  • Interactive tutorial that walks new developers through component props and immediate feedback

FAQ

Can I use this skill without Streamlit?

The builders are written for Streamlit-style rendering, but the patterns (artifact config, variation-driven previews, control-to-prop mapping) can be adapted to other frameworks like FastAPI frontends or custom web UIs.

How do I add new component types?

Extend the _render_component_with_props method or provide a ui_components wrapper that implements the desired render functions. Register new props in ComponentVariation for controls to map automatically.