Skip to content

Commit

Permalink
Prevent inherited styles on next/image wrapper or sizer (#30064)
Browse files Browse the repository at this point in the history
As a follow up to #30041 which changed `<div>` to `<span>`, this PR makes sure that unexpected styles are not applied to the image wrapper or sizer spans.

For example: `.content span {}` would apply to all spans and incorrectly style the image wrapper.

This PR adds [`all: initial`](https://developer.mozilla.org/en-US/docs/Web/CSS/all) to effectively reset the span styles.
  • Loading branch information
styfle committed Oct 19, 2021
1 parent f308b8a commit c4e26ab
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 47 deletions.
80 changes: 33 additions & 47 deletions packages/next/client/image.tsx
Expand Up @@ -458,10 +458,19 @@ export default function Image({
})
const isVisible = !isLazy || isIntersected

let wrapperStyle: JSX.IntrinsicElements['span']['style'] | undefined
let sizerStyle: JSX.IntrinsicElements['span']['style'] | undefined
const wrapperStyle: JSX.IntrinsicElements['span']['style'] = {
all: 'initial',
boxSizing: 'border-box',
overflow: 'hidden',
}
const sizerStyle: JSX.IntrinsicElements['span']['style'] = {
all: 'initial',
boxSizing: 'border-box',
display: 'block',
}
let hasSizer = false
let sizerSvg: string | undefined
let imgStyle: ImgElementStyle | undefined = {
const imgStyle: ImgElementStyle = {
position: 'absolute',
top: 0,
left: 0,
Expand Down Expand Up @@ -495,19 +504,12 @@ export default function Image({
: {}
if (layout === 'fill') {
// <Image src="i.png" layout="fill" />
wrapperStyle = {
display: 'block',
overflow: 'hidden',

position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,

boxSizing: 'border-box',
margin: 0,
}
wrapperStyle.display = 'block'
wrapperStyle.position = 'absolute'
wrapperStyle.top = 0
wrapperStyle.left = 0
wrapperStyle.bottom = 0
wrapperStyle.right = 0
} else if (
typeof widthInt !== 'undefined' &&
typeof heightInt !== 'undefined'
Expand All @@ -517,41 +519,24 @@ export default function Image({
const paddingTop = isNaN(quotient) ? '100%' : `${quotient * 100}%`
if (layout === 'responsive') {
// <Image src="i.png" width="100" height="100" layout="responsive" />
wrapperStyle = {
display: 'block',
overflow: 'hidden',
position: 'relative',

boxSizing: 'border-box',
margin: 0,
}
sizerStyle = { display: 'block', boxSizing: 'border-box', paddingTop }
wrapperStyle.display = 'block'
wrapperStyle.position = 'relative'
hasSizer = true
sizerStyle.paddingTop = paddingTop
} else if (layout === 'intrinsic') {
// <Image src="i.png" width="100" height="100" layout="intrinsic" />
wrapperStyle = {
display: 'inline-block',
maxWidth: '100%',
overflow: 'hidden',
position: 'relative',
boxSizing: 'border-box',
margin: 0,
}
sizerStyle = {
display: 'block',
boxSizing: 'border-box',
maxWidth: '100%',
}
wrapperStyle.display = 'inline-block'
wrapperStyle.position = 'relative'
wrapperStyle.maxWidth = '100%'
hasSizer = true
sizerStyle.maxWidth = '100%'
sizerSvg = `<svg width="${widthInt}" height="${heightInt}" xmlns="http://www.w3.org/2000/svg" version="1.1"/>`
} else if (layout === 'fixed') {
// <Image src="i.png" width="100" height="100" layout="fixed" />
wrapperStyle = {
display: 'inline-block',
overflow: 'hidden',
boxSizing: 'border-box',
position: 'relative',
width: widthInt,
height: heightInt,
}
wrapperStyle.display = 'inline-block'
wrapperStyle.position = 'relative'
wrapperStyle.width = widthInt
wrapperStyle.height = heightInt
}
} else {
// <Image src="i.png" />
Expand Down Expand Up @@ -584,11 +569,12 @@ export default function Image({

return (
<span style={wrapperStyle}>
{sizerStyle ? (
{hasSizer ? (
<span style={sizerStyle}>
{sizerSvg ? (
<img
style={{
all: 'initial',
maxWidth: '100%',
display: 'block',
margin: 0,
Expand Down
@@ -0,0 +1,42 @@
import React from 'react'
import Image from 'next/image'
import style from '../style.module.css'

const Page = () => {
return (
<div id="main-container" className={style.mainContainer}>
<h1>Image Style Inheritance</h1>
<Image
id="img-fixed"
layout="fixed"
src="/test.jpg"
width="400"
height="400"
/>

<Image
id="img-intrinsic"
layout="intrinsic"
src="/test.jpg"
width="400"
height="400"
/>

<div style={{ position: 'relative', width: '200px', height: '200px' }}>
<Image id="img-fill" layout="fill" src="/test.jpg" objectFit="cover" />
</div>

<Image
id="img-responsive"
layout="responsive"
src="/test.jpg"
width="400"
height="400"
/>

<footer>Footer</footer>
</div>
)
}

export default Page
12 changes: 12 additions & 0 deletions test/integration/image-component/default/style.module.css
@@ -1,3 +1,15 @@
.displayFlex {
display: flex;
}

.mainContainer {
border-radius: 75px;
}

.mainContainer span {
border-radius: 50px;
}

.mainContainer img {
border-radius: 100px;
}
40 changes: 40 additions & 0 deletions test/integration/image-component/default/test/index.test.js
Expand Up @@ -699,6 +699,46 @@ function runTests(mode) {
}
})

it('should apply style inheritance for img elements but not wrapper elements', async () => {
let browser
try {
browser = await webdriver(appPort, '/style-inheritance')

await browser.eval(
`document.querySelector("footer").scrollIntoView({behavior: "smooth"})`
)

const allImgs = await browser.eval(`
function foo() {
const imgs = document.querySelectorAll("img[id]");
for (let img of imgs) {
const br = window.getComputedStyle(img).getPropertyValue("border-radius");
if (!br) return 'no-border-radius';
if (br !== '100px') return br;
}
return true;
}()
`)
expect(allImgs).toBe(true)

const allSpans = await browser.eval(`
function foo() {
const spans = document.querySelectorAll("span");
for (let span of spans) {
const br = window.getComputedStyle(span).getPropertyValue("border-radius");
if (br && br !== '0px') return br;
}
return false;
}()
`)
expect(allSpans).toBe(false)
} finally {
if (browser) {
await browser.close()
}
}
})

// Tests that use the `unsized` attribute:
if (mode !== 'dev') {
it('should correctly rotate image', async () => {
Expand Down

0 comments on commit c4e26ab

Please sign in to comment.