@@ -81,50 +81,73 @@ export default function Carousel({
8181 }
8282
8383 if ( slide . classList . contains ( 'img-better' ) ) {
84- // Direct <Img> as slide: center naturally, don't fill container
84+ // Direct <Img> as slide: shrink-wrap to image at correct aspect ratio.
85+ // We explicitly compute image dimensions in JS rather than relying on
86+ // CSS auto + max-* + object-fit:contain, because the surrounding page
87+ // CSS (e.g. `.prose-content .img-better { margin: 1.25rem 0 }` in
88+ // PostLayout) and the wrapper's inline aspect-ratio interact with the
89+ // absolute positioning to produce visible top-cropping.
90+ const img = slide . querySelector ( 'img' ) ;
8591 if ( ! isAutoHeight ) {
86- // Clear inset so it doesn't stretch to fill
8792 slide . style . inset = '' ;
8893 slide . style . top = '50%' ;
8994 slide . style . left = '50%' ;
95+ slide . style . margin = '0' ;
9096 slide . style . transform = isActive
9197 ? 'translate(-50%, -50%) scale(1) translateY(0)'
9298 : 'translate(-50%, -50%) scale(0.97) translateY(4px)' ;
99+
100+ if ( img ) {
101+ const container = containerRef . current ;
102+ // Leave room for the wrapper's hover:ring-2 (2px) plus breathing
103+ // room, so the ring isn't clipped by the slides container's
104+ // overflow:hidden when it appears on hover.
105+ const RING_INSET = 8 ;
106+ const cw = Math . max ( 0 , ( container ?. clientWidth || 0 ) - RING_INSET * 2 ) ;
107+ const ch = Math . max ( 0 , ( container ?. clientHeight || 0 ) - RING_INSET * 2 ) ;
108+ const naturalW = parseInt ( img . getAttribute ( 'width' ) || '0' , 10 ) || img . naturalWidth || 1 ;
109+ const naturalH = parseInt ( img . getAttribute ( 'height' ) || '0' , 10 ) || img . naturalHeight || 1 ;
110+ const ratio = naturalW / naturalH ;
111+ let displayW : number ;
112+ let displayH : number ;
113+ if ( cw / ch > ratio ) {
114+ displayH = ch ;
115+ displayW = ch * ratio ;
116+ } else {
117+ displayW = cw ;
118+ displayH = cw / ratio ;
119+ }
120+ img . style . width = `${ displayW } px` ;
121+ img . style . height = `${ displayH } px` ;
122+ img . style . maxWidth = '' ;
123+ img . style . maxHeight = '' ;
124+ img . style . objectFit = '' ;
125+ slide . style . width = `${ displayW } px` ;
126+ slide . style . height = `${ displayH } px` ;
127+ }
93128 } else {
94129 slide . style . transform = isActive ? 'scale(1) translateY(0)' : 'scale(0.97) translateY(4px)' ;
130+ slide . style . width = 'auto' ;
131+ slide . style . height = 'auto' ;
132+ slide . style . margin = '' ;
133+ if ( img ) {
134+ img . style . maxWidth = '100%' ;
135+ img . style . maxHeight = '' ;
136+ img . style . width = '' ;
137+ img . style . height = '' ;
138+ img . style . objectFit = '' ;
139+ }
95140 }
96- slide . style . width = 'auto' ;
97- slide . style . height = 'auto' ;
98141 slide . style . maxWidth = '100%' ;
99142 slide . style . maxHeight = isAutoHeight ? '' : '100%' ;
100143 slide . style . aspectRatio = 'unset' ;
101- // Don't need flex centering since the div wraps the image tightly
102144 slide . style . display = '' ;
103145 slide . style . alignItems = '' ;
104146 slide . style . justifyContent = '' ;
105147 slide . style . overflow = '' ;
106148 slide . style . transition = isActive
107149 ? `opacity 420ms ${ EASE } , transform 420ms ${ EASE } , box-shadow 300ms ${ EASE } , visibility 0s 0s`
108150 : `opacity 420ms ${ EASE } , transform 420ms ${ EASE } , box-shadow 300ms ${ EASE } , visibility 0s 420ms` ;
109- const img = slide . querySelector ( 'img' ) ;
110- if ( img ) {
111- if ( isAutoHeight ) {
112- img . style . maxWidth = '100%' ;
113- img . style . maxHeight = '' ;
114- img . style . width = '' ;
115- img . style . height = '' ;
116- img . style . objectFit = '' ;
117- } else {
118- const container = containerRef . current ;
119- const cw = container ?. clientWidth || 0 ;
120- const ch = container ?. clientHeight || 0 ;
121- img . style . maxWidth = `${ cw } px` ;
122- img . style . maxHeight = `${ ch - 16 } px` ;
123- img . style . width = 'auto' ;
124- img . style . height = 'auto' ;
125- img . style . objectFit = 'contain' ;
126- }
127- }
128151 } else {
129152 // HTML content or wrapped images: center naturally
130153 slide . querySelectorAll < HTMLElement > ( '.img-better' ) . forEach ( ( el ) => {
0 commit comments