6161 canvas : canvas ,
6262 autoplay : true ,
6363 stateMachines : stateMachine ,
64+ autoBind : false , // Manual binding for theme switching
6465 layout : new rive . Layout ( {
6566 fit : rive . Fit [ fit ] , // Configurable fit mode
6667 alignment : rive . Alignment . Center
7778 // Set up dynamic cursor changes based on Rive's interactive areas
7879 setupDynamicCursor ( canvas , r , stateMachine ) ;
7980
81+ // Initialize with current site theme
82+ if ( r . viewModelCount > 0 ) {
83+ const currentIsDark = detectSiteTheme ( ) ;
84+ setRiveTheme ( r , currentIsDark , 'Palette' ) ;
85+ }
86+
8087 // Set up Rive event handling (primary method)
8188 try {
8289 if ( r . on && rive . EventType && rive . EventType . RiveEvent ) {
222229 // SDK Animation with native Rive interactions
223230 createRiveAnimation ( {
224231 canvasSelector : '#sdk-rive-canvas' ,
225- riveUrl : 'https://cdn.prod.website-files.com/67880ff570cdb1a85eee946f/68802bc752aef23fab76e6fc_sdk -rive.riv' ,
232+ riveUrl : 'https://cdn.prod.website-files.com/67880ff570cdb1a85eee946f/68802bc752aef23fab76e6fc_12235f640f9ad3339b42e8d026c6345a_sdk -rive.riv' ,
226233 aspectRatio : 369 / 93 ,
227234 stateMachine : "State Machine 1" ,
228235 fallbackImages : [
243250 // Docs Animation
244251 createRiveAnimation ( {
245252 canvasSelector : '#docs-rive-canvas' ,
246- riveUrl : 'https://cdn.prod.website-files.com/67880ff570cdb1a85eee946f/68825994c55f0eece04ce4e2_8014c1e8d9aa904e6d659541b198df0f_docs_animation .riv' ,
253+ riveUrl : 'https://cdn.prod.website-files.com/67880ff570cdb1a85eee946f/68825994c55f0eece04ce4e2_d261956a0f627eb6b94c39aa9fcc26f0_docs_animation .riv' ,
247254 aspectRatio : 404 / 262 ,
248255 stateMachine : "State Machine 1" ,
249256 fallbackImages : [
264271 // AI Animation with custom event handling
265272 createRiveAnimation ( {
266273 canvasSelector : '#ai-rive-canvas' ,
267- riveUrl : 'https://cdn.prod.website-files.com/67880ff570cdb1a85eee946f/68825e97fd6225e1c8a7488c_afec146ef95157cbef522bcdee1ba8d9_ai_animation .riv' ,
274+ riveUrl : 'https://cdn.prod.website-files.com/67880ff570cdb1a85eee946f/68825e97fd6225e1c8a7488c_b8d233d3b43c3da6eff8cc65874d7b49_ai_animation .riv' ,
268275 aspectRatio : 371 / 99 ,
269276 stateMachine : "State Machine 1" ,
270277 fallbackImages : [
289296 } ) ;
290297
291298 // Add additional Rive animations by calling createRiveAnimation() with your config
299+
300+ // Enable automatic site theme synchronization
301+ setupRiveSiteThemeSync ( ) ;
302+
303+ // Or manually control themes:
304+ // switchRiveThemes(true); // Switch to dark theme
305+ // switchRiveThemes(false); // Switch to light theme
292306 }
293307
294308 // Cleanup function to dispose of Rive instances
323337 }
324338 } ) . observe ( document , { subtree : true , childList : true } ) ;
325339
326- // Expose function globally for manual use
340+ // Data Binding approach using View Model Instances (Official Rive API)
341+ function setRiveTheme ( riveInstance , isDark , viewModelName = 'Palette' ) {
342+ try {
343+ // Get the view model by name
344+ const viewModel = riveInstance . viewModelByName ( viewModelName ) ;
345+ if ( ! viewModel ) {
346+ console . warn ( `View model '${ viewModelName } ' not found.` ) ;
347+ return false ;
348+ }
349+
350+ // Check if instances exist
351+ if ( viewModel . instanceCount === 0 ) {
352+ console . warn ( `No instances found in view model '${ viewModelName } '. Please create instances in the Rive editor.` ) ;
353+ return false ;
354+ }
355+
356+ let viewModelInstance = null ;
357+ const instanceName = isDark ? 'Dark' : 'Light' ;
358+
359+ // Try 1: Get instance by name (if names are set)
360+ try {
361+ viewModelInstance = viewModel . instanceByName ( instanceName ) ;
362+ } catch ( e ) {
363+ // Name-based access failed, continue to index-based
364+ }
365+
366+ // Try 2: Get instance by index (fallback for unnamed instances)
367+ if ( ! viewModelInstance ) {
368+ const instanceIndex = isDark ? 1 : 0 ; // Assume: 0=Light, 1=Dark
369+ if ( instanceIndex < viewModel . instanceCount ) {
370+ viewModelInstance = viewModel . instanceByIndex ( instanceIndex ) ;
371+ console . log ( `Using instance by index: ${ instanceIndex } (${ isDark ? 'Dark' : 'Light' } theme)` ) ;
372+ } else {
373+ console . warn ( `Instance index ${ instanceIndex } not available. Only ${ viewModel . instanceCount } instances found.` ) ;
374+ return false ;
375+ }
376+ }
377+
378+ if ( ! viewModelInstance ) {
379+ console . warn ( `Could not get ${ instanceName } theme instance.` ) ;
380+ return false ;
381+ }
382+
383+ // Bind the instance to the Rive file (this switches the theme)
384+ riveInstance . bindViewModelInstance ( viewModelInstance ) ;
385+ console . log ( `Successfully switched to ${ isDark ? 'Dark' : 'Light' } theme` ) ;
386+ return true ;
387+
388+ } catch ( e ) {
389+ console . warn ( 'Could not switch Rive theme instance:' , e ) ;
390+ return false ;
391+ }
392+ }
393+
394+ // Switch all animations to light/dark theme using Data Binding
395+ function switchAllRiveThemes ( isDark ) {
396+ document . querySelectorAll ( 'canvas[id$="rive-canvas"]' ) . forEach ( canvas => {
397+ if ( canvas . _riveInstance ) {
398+ setRiveTheme ( canvas . _riveInstance , isDark ) ;
399+ }
400+ } ) ;
401+ }
402+
403+ // Detect current site theme using multiple methods
404+ function detectSiteTheme ( ) {
405+ // Method 1: Check HTML/body classes (most common)
406+ if ( document . documentElement . classList . contains ( 'dark' ) ||
407+ document . body . classList . contains ( 'dark' ) ||
408+ document . documentElement . classList . contains ( 'dark-mode' ) ||
409+ document . body . classList . contains ( 'dark-mode' ) ) {
410+ return true ;
411+ }
412+
413+ // Method 2: Check data attributes
414+ if ( document . documentElement . getAttribute ( 'data-theme' ) === 'dark' ||
415+ document . body . getAttribute ( 'data-theme' ) === 'dark' ||
416+ document . documentElement . getAttribute ( 'theme' ) === 'dark' ) {
417+ return true ;
418+ }
419+
420+ // Method 3: Check CSS custom properties (if available)
421+ if ( typeof getComputedStyle !== 'undefined' ) {
422+ const style = getComputedStyle ( document . documentElement ) ;
423+ const colorScheme = style . getPropertyValue ( 'color-scheme' ) ;
424+ if ( colorScheme . includes ( 'dark' ) ) {
425+ return true ;
426+ }
427+ }
428+
429+ // Method 4: Fallback to system preference
430+ if ( window . matchMedia && window . matchMedia ( '(prefers-color-scheme: dark)' ) . matches ) {
431+ return true ;
432+ }
433+
434+ return false ; // Default to light theme
435+ }
436+
437+ // Set up comprehensive theme detection and syncing
438+ function setupRiveSiteThemeSync ( ) {
439+ // Set initial theme based on current site state
440+ const initialIsDark = detectSiteTheme ( ) ;
441+ switchAllRiveThemes ( initialIsDark ) ;
442+ console . log ( 'Rive themes initialized:' , initialIsDark ? 'Dark' : 'Light' ) ;
443+
444+ // Method 1: Watch for class changes on html/body (most reliable)
445+ const observer = new MutationObserver ( ( mutations ) => {
446+ mutations . forEach ( ( mutation ) => {
447+ if ( mutation . type === 'attributes' &&
448+ ( mutation . attributeName === 'class' ||
449+ mutation . attributeName === 'data-theme' ||
450+ mutation . attributeName === 'theme' ) ) {
451+ const isDark = detectSiteTheme ( ) ;
452+ switchAllRiveThemes ( isDark ) ;
453+ console . log ( 'Site theme changed, Rive themes updated:' , isDark ? 'Dark' : 'Light' ) ;
454+ }
455+ } ) ;
456+ } ) ;
457+
458+ // Observe both html and body for attribute changes
459+ observer . observe ( document . documentElement , {
460+ attributes : true ,
461+ attributeFilter : [ 'class' , 'data-theme' , 'theme' ]
462+ } ) ;
463+ observer . observe ( document . body , {
464+ attributes : true ,
465+ attributeFilter : [ 'class' , 'data-theme' , 'theme' ]
466+ } ) ;
467+
468+ // Method 2: Listen for system color scheme changes (backup)
469+ if ( window . matchMedia ) {
470+ const darkModeMediaQuery = window . matchMedia ( '(prefers-color-scheme: dark)' ) ;
471+ darkModeMediaQuery . addEventListener ( 'change' , ( e ) => {
472+ // Only apply system preference if no explicit site theme is set
473+ if ( ! document . documentElement . classList . contains ( 'dark' ) &&
474+ ! document . documentElement . classList . contains ( 'light' ) &&
475+ ! document . documentElement . getAttribute ( 'data-theme' ) ) {
476+ switchAllRiveThemes ( e . matches ) ;
477+ console . log ( 'System theme changed, Rive themes updated:' , e . matches ? 'Dark' : 'Light' ) ;
478+ }
479+ } ) ;
480+ }
481+
482+ // Method 3: Listen for custom theme events (if your site dispatches them)
483+ document . addEventListener ( 'themeChanged' , ( e ) => {
484+ if ( e . detail && typeof e . detail . isDark === 'boolean' ) {
485+ switchAllRiveThemes ( e . detail . isDark ) ;
486+ console . log ( 'Custom theme event received, Rive themes updated:' , e . detail . isDark ? 'Dark' : 'Light' ) ;
487+ }
488+ } ) ;
489+
490+ return observer ; // Return observer for cleanup if needed
491+ }
492+
493+ // Legacy function for backward compatibility
494+ function setupRiveSystemThemeDetection ( ) {
495+ return setupRiveSiteThemeSync ( ) ;
496+ }
497+
498+ // Expose functions globally for manual use
327499 window . createRiveAnimation = createRiveAnimation ;
328- } ) ( ) ;
500+ window . switchRiveThemes = switchAllRiveThemes ;
501+ window . setupRiveSystemThemeDetection = setupRiveSystemThemeDetection ;
502+ window . setupRiveSiteThemeSync = setupRiveSiteThemeSync ;
503+ } ) ( ) ;
0 commit comments