26

I'm developing a bunch of custom elements for an API using a web components polyfill and I've hit a snag.

One of the elements may contain an <img> or <canvas> element. If no dimensions are specified for the custom element, it should be the default size of the child element. If one or more dimensions are specified, they should be inherited by the child element.

For my first effort, I thought the CSS inherit value would be good enough:

my-el img, my-el canvas {
    width: inherit;
    height: inherit;
    max-width: inherit;
    min-width: inherit;
    max-height: inherit;
    min-height: inherit;
}

However, this doesn't work when a percentage is applied to the width or height for <my-el>. Instead, the child element takes up the same percentage of its parent.

I've tried various other attempts at a solution and searched far and wide to no avail. I think a pure CSS solution may be impossible but I'm hoping someone can prove otherwise.

To clarify, the end result should be that <my-el> behaves like an inline-replaced element itself, as if it were an <img> with its own internal dimensions. When you don't set width or height on those elements, they default to their internal dimensions. When you do set width or height, that takes precedence and any "internal" content is resized accordingly. (At least, I hope that clarifies!)

Andy E
  • 338,112
  • 86
  • 474
  • 445
  • 1
    `it should be the default size of the child element.` I am clearly misunderstading, but why wouldnt `width/height:100%` work for the child elements? – AmmarCSE Jun 12 '15 at 13:02
  • Never mind, I see now http://jsfiddle.net/6vygke92/1/ – AmmarCSE Jun 12 '15 at 13:03
  • Let's say the container is 400px wide, when the parent has a width of 50% (resolved to 200px) and the child inherits this value, what do you actually want? That the child has the same width as the parent **in pixels**? – sp00m Jun 15 '15 at 08:51
  • @sp00m, correct. I might have to clarify the question with more examples as it seems it's difficult to understand in it's present form. – Andy E Jun 15 '15 at 08:53
  • 1
    This problem could conceivably be solved with `my-el { display: contents; }` from [CSS Display 3](http://dev.w3.org/csswg/css-display). Miraculously, at least one implementation exists in Firefox 37+ allowing me to construct [this demo](http://jsfiddle.net/BoltClock/6vygke92/10). – BoltClock Jun 15 '15 at 09:52
  • @BoltClock: interesting! It really felt like there was a gap in the spec in this area, but I wasn't aware of this proposed feature. I don't have Firefox to hand on this machine, but from the description it does seem like it would solve the problem perfectly. – Andy E Jun 15 '15 at 10:00
  • @Andy E: The caveat is that `display: contents` on an element prevents the element from generating its own box altogether - in the case of my demo, my-el's border is no longer visible because box has been removed. You *might* be able to work around this with `my-el img, my-el canvas { all: inherit; }` - see [this other demo](http://jsfiddle.net/BoltClock/6vygke92/15) - but it would certainly take extensive testing to make sure it doesn't cause any unintended side effects. – BoltClock Jun 22 '15 at 04:30
  • @BoltClock: indeed, and there's also another complexity; if it doesn't have a box it can't be positioned properly. This makes it awkward if you want to have an overlay element (see [updated demo](http://jsfiddle.net/6vygke92/16/)). – Andy E Jun 22 '15 at 08:18

6 Answers6

7

You could use min-width:100% - min-height:100% as a fallback for the situation in which a percentage width is assigned to parent div:

 my-el img, my-el canvas {
   width: inherit;
   height: inherit;
   max-width: inherit;
   min-width:100%;
   max-height: inherit;
   min-height: 100%;
 }

fiddle

Andy E
  • 338,112
  • 86
  • 474
  • 445
maioman
  • 18,154
  • 4
  • 36
  • 42
  • hmm, we may have a winner here. I had to also apply `min-height: 100%` as the same fallback for the y dimensions, but a brief play around and I can't seem to find any edge case that it doesn't cover. I'll play some more when I get some free time and if it covers all bases you'll get the tick. – Andy E Jun 15 '15 at 09:23
  • I've played a little bit with it and I couldn't find a scenario in which it doesn't work... – maioman Jun 15 '15 at 09:26
  • 1
    I've switched to @Pangloss's answer as it is technically better. However, I've added another bounty that I'll award to you tomorrow to make up for it. – Andy E Jun 22 '15 at 08:22
6

The percentage width and height are relative to the parent element, so you could probably just use this I think.

my-el {
    display: inline-block;
}
my-el img, my-el canvas {
    width: 100%;
    height: 100%;
}

Feel free to play around by setting any width and height to my-el, see if it works for you.

http://jsfiddle.net/6afdbfck/

Stickers
  • 75,527
  • 23
  • 147
  • 186
  • 1
    Embarrassingly, I think this may do the job. It was the first thing I tried and, after a lot of frustration and asking this question, I discovered that `display: block` was set on the custom element and that was conflicting. – Andy E Jun 18 '15 at 16:09
2

Unless I totally misunderstand your requirements then all you need is:

my-el {display: inline-block;}

To ensure the child element adopts the attributes of the parent use:

my-el > img,
my-el > canvas {
    height: auto;
    max-width: 100%;
    width: 100%;
}

FWIW, it's probably better to add a class to your element, as a hook, and attach your styles to that rather than target the element directly, e.g:

<myelement class="my-el"><img></myelement>

.my-el {display: inline-block;}

.my-el > img,
.my-el > canvas {
    height: auto;
    max-width: 100%;
    width: 100%;
 }

If you want these styles to apply to any element that is a direct child of your parent element then just use the universal selector:

.my-el > * {
    height: auto;
    max-width: 100%;
    width: 100%;
 }
Seth Warburton
  • 2,234
  • 1
  • 16
  • 17
  • 1
    The problem is that an API user might want to specify their own dimensions on `my-el` (which is a custom element, not a class), which would not be inherited by the `img`, `canvas` or whatever I might want it to be. I'm trying to hide the implementation details from the user, so they might treat it as if it were an inline-replaced element itself, just like `img` or `canvas`. – Andy E Jun 15 '15 at 08:12
  • 2
    Right, so you want the child to inherit the size of its parent instead. Your question read the other way around. I'll update my answer. – Seth Warburton Jun 15 '15 at 08:43
  • Using a class defeats the point of it being a custom element (and makes no real difference either). Also, your suggestion [doesn't really work](http://jsfiddle.net/6vygke92/9/), `height: auto` means that the img will never adopt a `height` set by the parent. – Andy E Jun 15 '15 at 09:09
  • `height: auto;` ensures that your image is never distorted to fit the height of the parent, a situation I'm certain you'd need to avoid in production. Using a class for styling gives higher specificity which could help unwanted and unintended clashes, and adds robustness allowing you to make changes to your element name without having to rewrite your styles. – Seth Warburton Jun 15 '15 at 11:55
  • I think you're missing the point of why I'm providing a custom element as part of a library. Using a class would detract from the transparency of using a custom element; if a third-party wanted to manipulate the element, they'd need to ensure the class remained intact. Also, specificity needs to be lower so that styles can be overridden easily. Think of the styles I'm applying as akin to default styles for an element provided by a browser. Those have the lowest specificity possible so that any dev-defined CSS rule will override them. – Andy E Jun 15 '15 at 13:48
0

I think I don't understand the question. If you are able to set inline styles on the img/canvas then you should be sorted using width/height: 100% as these will be overriden by the inline-styles.

I included a JSFiddle where I set inline-styles on one of the elements and the other two which take the parents'.

http://jsfiddle.net/6vygke92/5/

.my-el img, .my-el canvas {    
    display: block; 
    width: 100%;
    height: 100%;
    max-width: inherit;
    min-width: inherit;
    max-height: inherit;
    min-height: inherit;
}
Kevin Farrugia
  • 6,431
  • 4
  • 38
  • 61
0

How do your clients set the width or height on the custom element? Anyway, you can do this:

my-el img, my-el canvas {
    display: block;
    width: auto;
    height: auto;
    max-width: 100%;
    max-height: 100%;
}

It says the img/canvas to be as wide as their default is, but to not be wider than their parent. Images will keep their aspect ratios, leaving empty space below them or on the right side, as you can see in this fiddle http://jsfiddle.net/vendruscolo/yrvfz1fw/9/

If you want images to be stretched to fill the available space you can instead to this

my-el img, my-el canvas {
    display: block;
    width: 100%;
    height: 100%;
}

As long as you size elements with percentages or with smaller widths, you're fine. When you set an explicit (larger) width using pixels the image will stop to maintain its aspect ratio, as you can see http://jsfiddle.net/vendruscolo/zz6gnytt/2/ In that case, if you want to maintain the aspect ratio you'll have to query the naturalWidth and naturalHeight of the image and do your own math to keep the aspect ratio.

Alessandro Vendruscolo
  • 14,493
  • 4
  • 32
  • 41
  • The clients would use their own CSS stylesheet to apply styles. I want the element to be as transparent as possible for them. Your solution doesn't work if `height` or `width` are larger on the custom element than the img/canvas element. http://jsfiddle.net/yrvfz1fw/3/ – Andy E Jun 15 '15 at 09:14
  • We can add `height: 100%` and set `display: block` for images (canvas are blocks by default). Do you have to enforce a given (or the image) aspect ratio? http://jsfiddle.net/vendruscolo/yrvfz1fw/5/ – Alessandro Vendruscolo Jun 15 '15 at 09:19
  • canvas elements _aren't_ block elements by default, they're inline [replaced](https://developer.mozilla.org/en-US/docs/Web/CSS/Replaced_element) elements, and your solution is broken as the aspect ratio of the img isn't kept when there are no dimensions set on the custom element itself. – Andy E Jun 15 '15 at 09:32
  • Here https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements they say it's a block element, but that's not the point. I've updated my answer to take in account the aspect ratio of images. – Alessandro Vendruscolo Jun 15 '15 at 09:55
  • you're right, they are block elements (although they are still replaced, which is what confused me). – Andy E Jun 15 '15 at 10:03
0

I hope this time my code will works

my-el {
    width: 50%;
    border: 1px solid silver;
}

my-el img, my-el canvas {
    width: inherit;
    height: inherit;
    max-width: inherit;
    min-width: inherit;
    max-height: inherit;
    min-height: inherit;
    position: relative;
}
<my-el>
  <img src="https://s3-eu-west-1.amazonaws.com/uploads-eu.hipchat.com/214790/1500400/lX8QKASWLAKotii/29e222d6-5828-4c3e-8153-5d5d065e91b1-original.png" />
</my-el>

Or another way to do it

my-el {
  width: 50%;
  border: 1px solid silver;
}
my-el img,
my-el canvas {
  width: inherit;
  height: inherit;
  max-width: inherit;
  min-width: inherit;
  max-height: inherit;
  min-height: inherit;
  position: absolute;
  right: 0;
  left: 0;
  top: 0;
  bottom: 0;
}
<my-el>
  <img src="https://s3-eu-west-1.amazonaws.com/uploads-eu.hipchat.com/214790/1500400/lX8QKASWLAKotii/29e222d6-5828-4c3e-8153-5d5d065e91b1-original.png" />
</my-el>

I hope this time will help you with an easy way and less code and compatible with all browsers as well as responsive too, Also it's exactly, perfect and doesn't show the border of my-el {}. Let me know if you have any question.

  • This falls flat once you have any other content on the page, e.g. if there is a non-positioned ancestor that has its own width, or if there is *any* other content following my-el and its child that are expecting it to be in flow. In general, absolutely positioning an element without changing *anything else* on the page is not a good idea because it can and will have adverse effects on layout. – BoltClock Jun 22 '15 at 04:40
  • Also, please stop flagging your answers for moderator attention unless you understand what exactly a moderator does. The flag link is not a toy. – BoltClock Jun 22 '15 at 04:46
  • Thanks for your advice "The flag link is not a toy", Can you please provide the problem with other content because I'm not able to figure out that? Thanks – Mohammed Moustafa Jun 22 '15 at 05:04
  • Can you see my codes I have provided another 2 ways? – Mohammed Moustafa Jun 22 '15 at 07:56