@@ -318,18 +318,27 @@ export function pluginFullscreen(options: FullscreenPluginOptions = {}) {
318318 z-index: 100;
319319 }
320320
321+ /* Push all buttons in figcaption to the right as a group */
322+ figcaption button:first-of-type {
323+ margin-inline-start: auto !important;
324+ }
325+
321326 /* When inside figcaption header, use flex-compatible relative positioning and always visible */
322327 figcaption .cb-fullscreen__button,
323328 .header .cb-fullscreen__button {
324329 position: relative !important;
325330 top: auto !important;
326331 right: auto !important;
327- margin-inline-start: auto;
328332 margin-inline-end: 0.5rem;
329333 opacity: 1 !important;
330334 transform: none !important;
331335 }
332336
337+ /* Push button down slightly for titled non-terminal blocks */
338+ .expressive-code.has-title:not(.is-terminal) figcaption .cb-fullscreen__button {
339+ margin-top: 0.25rem;
340+ }
341+
333342 /* Override hover effects for header buttons - keep them stable */
334343 figcaption .cb-fullscreen__button:hover,
335344 .header .cb-fullscreen__button:hover {
@@ -339,22 +348,22 @@ export function pluginFullscreen(options: FullscreenPluginOptions = {}) {
339348
340349 /* Hover-only visibility for untitled, non-terminal blocks (only buttons outside figcaption) */
341350 ${ config . showOnHoverOnly ? `
342- .expressive-code:not(.has-title) .frame :not(.is-terminal) > .cb-fullscreen__button {
351+ .expressive-code:not(.has-title):not(.is-terminal) > .cb-fullscreen__button {
343352 opacity: 0;
344353 transition: opacity 0.2s ease, background-color 0.2s, border-color 0.2s, transform 0.2s ease;
345354 }
346355
347- .expressive-code:not(.has-title):hover .frame: not(.is-terminal) > .cb-fullscreen__button,
348- .expressive-code:not(.has-title) .frame :not(.is-terminal) > .cb-fullscreen__button:focus,
349- .expressive-code:not(.has-title) .frame :not(.is-terminal) > .cb-fullscreen__button:focus-visible {
356+ .expressive-code:not(.has-title):not(.is-terminal):hover > .cb-fullscreen__button,
357+ .expressive-code:not(.has-title):not(.is-terminal) > .cb-fullscreen__button:focus,
358+ .expressive-code:not(.has-title):not(.is-terminal) > .cb-fullscreen__button:focus-visible {
350359 opacity: 0.7;
351360 border: 2px solid #888888 !important;
352361 border-radius: 0.25rem !important;
353362 }
354363
355364 /* Mobile/touch device fallback - show button on touch devices */
356365 @media (hover: none) and (pointer: coarse) {
357- .expressive-code:not(.has-title) .frame :not(.is-terminal) > .cb-fullscreen__button {
366+ .expressive-code:not(.has-title):not(.is-terminal) > .cb-fullscreen__button {
358367 opacity: 0.7;
359368 }
360369 }
@@ -528,14 +537,21 @@ export function pluginFullscreen(options: FullscreenPluginOptions = {}) {
528537 return ;
529538 }
530539
531- const figcaption = frameElement . children ?. find (
532- ( child : any ) => child . type === 'element' && child . tagName === 'figcaption'
533- ) ;
534-
535- if ( figcaption && figcaption . type === 'element' ) {
536- figcaption . children = figcaption . children || [ ] ;
537- figcaption . children . push ( fullscreenButton ) ;
540+ // Only append to figcaption if the frame has a visible header (title or terminal)
541+ if ( hasTitle || isTerminal ) {
542+ const figcaption = frameElement . children ?. find (
543+ ( child : any ) => child . type === 'element' && child . tagName === 'figcaption'
544+ ) ;
545+
546+ if ( figcaption && figcaption . type === 'element' ) {
547+ figcaption . children = figcaption . children || [ ] ;
548+ figcaption . children . push ( fullscreenButton ) ;
549+ } else {
550+ frameElement . children = frameElement . children || [ ] ;
551+ frameElement . children . push ( fullscreenButton ) ;
552+ }
538553 } else {
554+ // For untitled, non-terminal blocks, append to frame for absolute positioning
539555 frameElement . children = frameElement . children || [ ] ;
540556 frameElement . children . push ( fullscreenButton ) ;
541557 }
@@ -777,6 +793,16 @@ export function pluginFullscreen(options: FullscreenPluginOptions = {}) {
777793 clonedBlock.classList.add('expressive-code');
778794 }
779795
796+ // Auto-expand collapsed code blocks in fullscreen (for expressive-code-collapsible compatibility)
797+ const collapseWrapper = clonedBlock.querySelector('.ec-collapse--collapsed');
798+ if (collapseWrapper) {
799+ collapseWrapper.classList.remove('ec-collapse--collapsed');
800+ collapseWrapper.classList.add('ec-collapse--expanded');
801+
802+ // Update aria-expanded on collapse toggle buttons
803+ const collapseButtons = collapseWrapper.querySelectorAll('.ec-collapse__toggle, .ec-collapse__header-toggle');
804+ collapseButtons.forEach(btn => btn.setAttribute('aria-expanded', 'true'));
805+ }
780806
781807 // Force full width with inline styles.
782808 // Styles handled in baseStyles (.expressive-code.cb-fullscreen__active).
0 commit comments