web-embed Application
Embeddable Widgets for External Sites
Application Type : Embeddable Widget Platform
Port : 3003
Users : External website visitors
Status : Planned
Technology : Vanilla JS + React + @reptidex/core
Purpose & Responsibilities
web-embed provides lightweight, embeddable widgets that allow reptile breeders to integrate reptidex functionality directly into their own websites. It focuses on minimal bundle size, maximum compatibility, and seamless integration.
Core Features
Animal Profile Widgets
Compact animal profile cards
Photo galleries with lightbox
Basic animal information display
Contact breeder functionality
Responsive design for all devices
Pedigree Widgets
Interactive pedigree tree display
Multi-generation lineage visualization
Collapsible/expandable tree nodes
Print and export functionality
Mobile-optimized navigation
Breeder Showcase
Breeder profile summary cards
Featured animals carousel
Recent breeding announcements
Contact information and links
Social media integration
Marketplace Listings
Available animals grid display
Filtering and search functionality
Price display and availability
Quick inquiry forms
Real-time availability updates
Architecture & Technology
Lightweight Technology Stack
Core Technologies :
Vanilla JavaScript for core functionality
React 18 for widget components (optional)
TypeScript for type safety
Minimal CSS framework for styling
Web Components for framework agnostic widgets
Bundle Optimization :
@reptidex/core : Essential API clients only
Tree-shaken imports for minimal bundle size
Dynamic imports for advanced features
CSS-in-JS for scoped styling
Service worker for caching
src/
├── widgets/ # Individual widget components
│ ├── animal-profile/ # Animal profile card widget
│ │ ├── AnimalProfile.tsx
│ │ ├── AnimalProfile.css
│ │ └── index.ts
│ ├── pedigree-tree/ # Pedigree visualization widget
│ │ ├── PedigreeTree.tsx
│ │ ├── PedigreeTree.css
│ │ └── index.ts
│ ├── breeder-showcase/ # Breeder showcase widget
│ │ ├── BreederShowcase.tsx
│ │ ├── BreederShowcase.css
│ │ └── index.ts
│ └── marketplace-grid/ # Marketplace listing widget
│ ├── MarketplaceGrid.tsx
│ ├── MarketplaceGrid.css
│ └── index.ts
├── core/ # Core widget functionality
│ ├── widget-loader.ts # Dynamic widget loading
│ ├── theme-system.ts # Theming and customization
│ ├── api-client.ts # Lightweight API client
│ └── error-handler.ts # Error handling and fallbacks
├── embed/ # Embed scripts and setup
│ ├── embed.ts # Main embed script
│ ├── widget-config.ts # Widget configuration
│ └── fallbacks.ts # Fallback components
└── build/ # Build outputs
├── widgets/ # Individual widget bundles
├── embed.min.js # Main embed script
└── themes/ # Pre-built themes
Animal Profile Card Component
Animal Profile Widget :interface AnimalProfileProps {
animalId : string ;
theme ?: 'light' | 'dark' | 'custom' ;
showContact ?: boolean ;
showPedigree ?: boolean ;
maxWidth ?: string ;
}
const AnimalProfileWidget = ({
animalId ,
theme = 'light' ,
showContact = true ,
showPedigree = false ,
maxWidth = '400px'
} : AnimalProfileProps ) => {
const { data : animal , isLoading , error } = useAnimal ( animalId );
if ( isLoading ) return < SkeletonCard />;
if ( error ) return < ErrorFallback />;
if ( ! animal ) return < NotFoundFallback />;
return (
< WidgetContainer theme = { theme } maxWidth = { maxWidth } >
< AnimalImage
src = {animal. primaryImage }
alt = {animal. name }
fallback = "/placeholder-animal.jpg"
/>
< AnimalInfo >
< AnimalName >{animal. name } </ AnimalName >
< SpeciesInfo >{animal.species. commonName } </ SpeciesInfo >
< GeneticsDisplay genetics = {animal. genetics } />
{ animal . price && (
< PriceTag price = {animal. price } currency = "USD" />
)}
</ AnimalInfo >
{ showContact && (
< ContactSection >
< ContactButton
breederId = {animal. breederId }
animalId = {animal. id }
/>
</ ContactSection >
)}
{ showPedigree && animal . hasLineage && (
< PedigreeLink animalId = {animal. id } />
)}
< PoweredBy />
</ WidgetContainer >
);
};
Integration Example :<!-- Simple integration -->
< div id = "reptidex-animal-123" ></ div >
< script >
reptidex . renderAnimalProfile ( 'reptidex-animal-123' , {
animalId: '123' ,
theme: 'light' ,
showContact: true
});
</ script >
Interactive Pedigree Visualization
Pedigree Tree Component :interface PedigreeTreeProps {
animalId : string ;
generations ?: number ;
theme ?: ThemeConfig ;
interactive ?: boolean ;
showPhotos ?: boolean ;
compact ?: boolean ;
}
const PedigreeTreeWidget = ({
animalId ,
generations = 3 ,
theme ,
interactive = true ,
showPhotos = true ,
compact = false
} : PedigreeTreeProps ) => {
const { data : pedigree } = usePedigree ( animalId , generations );
const [ selectedNode , setSelectedNode ] = useState < string | null >( null );
return (
< PedigreeContainer theme = { theme } compact = { compact } >
< PedigreeHeader >
< GenerationControls
value = { generations }
onChange = { handleGenerationChange }
/>
< ExportControls onExport = { handleExport } />
</ PedigreeHeader >
< PedigreeTree
data = { pedigree }
interactive = { interactive }
showPhotos = { showPhotos }
selectedNode = { selectedNode }
onNodeClick = { setSelectedNode }
onNodeHover = { handleNodeHover }
/>
{ selectedNode && (
< NodeDetails
animal = {pedigree.getNode(selectedNode)}
onClose = {() => setSelectedNode ( null )}
/>
)}
< PoweredBy />
</ PedigreeContainer >
);
};
Mobile Optimization :const ResponsivePedigreeTree = ( props : PedigreeTreeProps ) => {
const isMobile = useMediaQuery ( '(max-width: 768px)' );
return isMobile ? (
< MobilePedigreeView { ... props } />
) : (
< DesktopPedigreeView { ... props } />
);
};
Marketplace Listing Display
Marketplace Grid Component :interface MarketplaceGridProps {
breederId ?: string ;
species ?: string [];
maxItems ?: number ;
showFilters ?: boolean ;
gridColumns ?: number ;
theme ?: ThemeConfig ;
}
const MarketplaceGridWidget = ({
breederId ,
species ,
maxItems = 12 ,
showFilters = true ,
gridColumns = 3 ,
theme
} : MarketplaceGridProps ) => {
const [ filters , setFilters ] = useState ({
species: species || [],
priceRange: null ,
availability: 'available'
});
const { data : listings } = useMarketplaceListings ({
breederId ,
filters ,
limit: maxItems
});
return (
< MarketplaceContainer theme = { theme } >
{ showFilters && (
< FilterBar filters = { filters } onChange = { setFilters } />
)}
< AnimalGrid columns = { gridColumns } >
{ listings ?. map ( listing => (
< AnimalCard
key = {listing. id }
animal = {listing. animal }
price = {listing. price }
availability = {listing. availability }
onInquiry = {() => handleInquiry (listing.id)}
/>
))}
</ AnimalGrid >
{ listings ?. length === 0 && (
< EmptyState message = "No animals available" />
)}
< PoweredBy />
</ MarketplaceContainer >
);
};
Embed System & Integration
Theme System & Customization
Theme Configuration :interface ThemeConfig {
colors : {
primary : string ;
secondary : string ;
background : string ;
text : string ;
border : string ;
accent : string ;
};
typography : {
fontFamily : string ;
fontSize : {
small : string ;
medium : string ;
large : string ;
};
fontWeight : {
normal : number ;
medium : number ;
bold : number ;
};
};
spacing : {
small : string ;
medium : string ;
large : string ;
};
borderRadius : string ;
shadows : {
small : string ;
medium : string ;
large : string ;
};
}
const createTheme = ( customTheme : Partial < ThemeConfig >) : ThemeConfig => {
return deepMerge ( defaultTheme , customTheme );
};
// CSS Custom Properties for theming
const applyTheme = ( theme : ThemeConfig , container : HTMLElement ) => {
Object . entries ( flattenTheme ( theme )). forEach (([ property , value ]) => {
container . style . setProperty ( `--reptidex- ${ property } ` , value );
});
};
CSS Integration :/* Widget base styles */
.reptidex-widget {
--reptidex-primary : #2563eb ;
--reptidex-secondary : #64748b ;
--reptidex-background : #ffffff ;
--reptidex-text : #1e293b ;
--reptidex-border : #e2e8f0 ;
font-family : var ( --reptidex-font-family , system-ui , sans-serif );
background : var ( --reptidex-background );
color : var ( --reptidex-text );
border : 1 px solid var ( --reptidex-border );
border-radius : var ( --reptidex-border-radius , 8 px );
}
.reptidex-widget.dark {
--reptidex-background : #1e293b ;
--reptidex-text : #f1f5f9 ;
--reptidex-border : #475569 ;
}
Bundle Size Optimization
Code Splitting & Tree Shaking :// Webpack configuration for optimal bundling
const webpackConfig = {
entry: {
'embed' : './src/embed/embed.ts' ,
'animal-profile' : './src/widgets/animal-profile/index.ts' ,
'pedigree-tree' : './src/widgets/pedigree-tree/index.ts' ,
'breeder-showcase' : './src/widgets/breeder-showcase/index.ts' ,
'marketplace-grid' : './src/widgets/marketplace-grid/index.ts'
},
output: {
filename: '[name].min.js' ,
chunkFilename: 'chunks/[name].[contenthash].js' ,
library: 'reptidex' ,
libraryTarget: 'umd'
},
optimization: {
usedExports: true ,
sideEffects: false ,
splitChunks: {
chunks: 'all' ,
cacheGroups: {
vendor: {
test: / [ \\ / ] node_modules [ \\ / ] / ,
name: 'vendor' ,
chunks: 'all'
}
}
}
}
};
Bundle Size Targets :
Main embed script: < 5KB gzipped
Individual widgets: < 15KB gzipped each
Shared vendor chunks: < 30KB gzipped
Total page impact: < 50KB gzipped
Loading & Caching Strategy
Integration Examples
WordPress Integration
WordPress Shortcode :// reptidex WordPress Plugin
function reptidex_animal_profile_shortcode ( $atts ) {
$atts = shortcode_atts ( array (
'animal_id' => '' ,
'theme' => 'light' ,
'show_contact' => 'true' ,
'show_pedigree' => 'false' ,
'max_width' => '400px'
), $atts );
$widget_id = 'reptidex-animal-' . $atts [ 'animal_id' ];
ob_start ();
?>
< div id = "<?php echo esc_attr( $widget_id ); ?>" ></ div >
< script >
document . addEventListener ( 'DOMContentLoaded' , function () {
reptidex . renderAnimalProfile ( '<?php echo esc_js($widget_id); ?>' , {
animalId : '<?php echo esc_js($atts[' animal_id ']); ?>' ,
theme : '<?php echo esc_js($atts[' theme ']); ?>' ,
showContact : <? php echo $atts [ 'show_contact' ] === 'true' ? 'true' : 'false' ; ?> ,
showPedigree : <? php echo $atts [ 'show_pedigree' ] === 'true' ? 'true' : 'false' ; ?> ,
maxWidth : '<?php echo esc_js($atts[' max_width ']); ?>'
});
});
</ script >
<? php
return ob_get_clean ();
}
add_shortcode ( 'reptidex_animal' , 'reptidex_animal_profile_shortcode' );
Usage in WordPress :[reptidex_animal animal_id="123" theme="dark" show_contact="true"]
Shopify Integration
E-commerce Platform Integration
Error Handling & Fallbacks
Graceful Degradation
Error Boundary Implementation :class WidgetErrorBoundary extends React . Component < Props , State > {
constructor ( props : Props ) {
super ( props );
this . state = { hasError: false , error: null };
}
static getDerivedStateFromError ( error : Error ) : State {
return { hasError: true , error };
}
componentDidCatch ( error : Error , errorInfo : React . ErrorInfo ) {
// Log error to reptidex analytics
this . logErrorToService ( error , errorInfo );
}
private logErrorToService ( error : Error , errorInfo : React . ErrorInfo ) {
fetch ( ` ${ API_BASE } /widget-errors` , {
method: 'POST' ,
headers: { 'Content-Type' : 'application/json' },
body: JSON . stringify ({
error: error . message ,
stack: error . stack ,
componentStack: errorInfo . componentStack ,
timestamp: new Date (). toISOString (),
url: window . location . href ,
userAgent: navigator . userAgent
})
}). catch (() => {
// Silently fail if error logging fails
});
}
render () {
if ( this . state . hasError ) {
return (
< ErrorFallback
error = {this.state. error }
onRetry = {() => this.setState({ hasError : false , error : null })}
/>
);
}
return this . props . children ;
}
}
Fallback Components :const ErrorFallback = ({ error , onRetry } : ErrorFallbackProps ) => (
< div className = "reptidex-error-fallback" >
< div className = "error-icon" > ⚠️ </ div >
< h3 > Unable to load reptidex widget </ h3 >
< p > There was an error loading this content . </ p >
< button onClick = { onRetry } className = "retry-button" >
Try Again
</ button >
< div className = "powered-by" >
Powered by < a href = "https://reptidex.com" > reptidex </ a >
</ div >
</ div >
);
const OfflineFallback = () => (
< div className = "reptidex-offline-fallback" >
< div className = "offline-icon" > 📡 </ div >
< h3 > Content temporarily unavailable </ h3 >
< p > Please check your internet connection and try again . </ p >
</ div >
);
Security & Privacy
Content Security Policy :// CSP-safe implementation
const SecureWidget = ({ config } : WidgetProps ) => {
// Sanitize all user inputs
const sanitizedConfig = sanitizeConfig ( config );
// Use nonce-based inline scripts
const scriptNonce = generateNonce ();
return (
< WidgetContainer >
< WidgetContent config = { sanitizedConfig } />
< script nonce = { scriptNonce } >
{ generateSecureScript ( sanitizedConfig )}
</ script >
</ WidgetContainer >
);
};
const sanitizeConfig = ( config : any ) : WidgetConfig => {
return {
animalId: validateUUID ( config . animalId ),
theme: validateTheme ( config . theme ),
maxWidth: validateCSSValue ( config . maxWidth ),
// ... other validations
};
};
Data Privacy :
No tracking cookies without consent
Minimal data collection
GDPR compliance for EU visitors
Configurable privacy settings
Secure API communications (HTTPS only)
Testing & Quality Assurance
Testing Framework :describe ( 'Animal Profile Widget' , () => {
beforeEach (() => {
// Mock API responses
mockAPI . setup ();
});
it ( 'renders correctly with valid animal ID' , async () => {
const container = document . createElement ( 'div' );
await reptidex . renderAnimalProfile ( container . id , {
animalId: 'valid-id' ,
theme: 'light'
});
expect ( container . querySelector ( '.animal-name' )). toBeInTheDocument ();
expect ( container . querySelector ( '.animal-image' )). toBeInTheDocument ();
});
it ( 'shows error fallback for invalid animal ID' , async () => {
const container = document . createElement ( 'div' );
await reptidex . renderAnimalProfile ( container . id , {
animalId: 'invalid-id'
});
expect ( container . querySelector ( '.reptidex-error-fallback' )). toBeInTheDocument ();
});
it ( 'handles network errors gracefully' , async () => {
mockAPI . mockNetworkError ();
const container = document . createElement ( 'div' );
await reptidex . renderAnimalProfile ( container . id , {
animalId: 'valid-id'
});
expect ( container . querySelector ( '.reptidex-offline-fallback' )). toBeInTheDocument ();
});
});
Cross-Browser Compatibility :
Chrome 90+, Firefox 88+, Safari 14+, Edge 90+
Mobile browsers (iOS Safari, Chrome Mobile)
Internet Explorer 11 (with polyfills)
Testing with BrowserStack automation
Deployment & Distribution
CDN Distribution
Global Widget Distribution
web-embed extends reptidex’s reach by enabling seamless integration into external websites while maintaining performance, security, and user experience standards.