Are there options for cropping inline-block line breaks in CSS? - css

I have an interesting issue I've never encountered in css before. A client is asking if the first blue box in the image below can crop after the word "Association" instead of spanning the full width of the parent.
That's an inline-block element with width set to auto.
display: inline-block;
width: auto;
padding: .5em;
Nothing special, right? As you can see by box 2, The director of marketing has his title cropped correctly. So why is it that css chooses to display the blue background nearly the full width of the parent when the text requires more than one line?
Does anyone know of some css property that can be applied to make an inline-block element crop at the end of words? Forgive if I am failing to articulate this. I'm not really sure how to structure this question.
If it helps to know, I cannot make a special case for this item. It is a CMS based system and the job titles can be anything. Making a special case, like max-width:90% wouldn't work, especially considering mobile break points and such. We would have to write cases for anything with a long title, and specific to that title's unique set of words, so that's pretty much out of the question.
Here's a fiddle to mess with if you want to see it in action. They styles are pretty much the same, at least the important parts.
https://jsfiddle.net/4f7jj7L8/

If the CMS entry itself can have the line break (not a br tag but a normal linebreak), then there's a simple solution:
CSS:
.job > h5 {
white-space: pre-line;
}
HTML (Generated):
<h5>Director
of Association Management Services</h5>
Demo: https://jsfiddle.net/4f7jj7L8/4/

Not automatically.
The width of an inline-block is defined as
If 'width' is 'auto', the used value is the shrink-to-fit width as for
floating elements.
Calculation of the shrink-to-fit width is similar to calculating the
width of a table cell using the automatic table layout algorithm.
Roughly: calculate the preferred width by formatting the content
without breaking lines other than where explicit line breaks occur,
and also calculate the preferred minimum width, e.g., by trying all
possible line breaks. CSS 2.1 does not define the exact algorithm.
Thirdly, find the available width: in this case, this is the width of
the containing block minus the used values of 'margin-left',
'border-left-width', 'padding-left', 'padding-right',
'border-right-width', 'margin-right', and the widths of any relevant
scroll bars.
Then the shrink-to-fit width is: min(max(preferred minimum width,
available width), preferred width).
If the text is long enough, the preferred width will be greater than the available width. So the inline-block will fill all the available width.
However, you can avoid that inserting explicit line breaks (e.g. <br />) at the desired places.
.job {
width: 45%;
float: left;
margin-right: 2%;
}
.image {
width: 100%;
height: 100px;
background: #000;
}
h2 {
font-size: 1.2em;
}
h5 {
display: inline-block;
padding: .5em;
font-size: 1em;
background: #ff0000;
}
p {
margin-top: 1em;
}
<div class="job">
<div class="image"></div>
<h2>Some Name</h2>
<h5>Director<br />of<br />Association<br />Management<br />Services</h5>
<p>Lorem ipsum dolor set amit</p>
</div>
<div class="job">
<div class="image"></div>
<h2>Some Name</h2>
<h5>Director of Sales</h5>
<p>Lorem ipsum dolor set amit</p>
</div>

Yes, it is possible — the highlight effect can be cleverly emulated using a mix of:
Declaring the element as inline, so it collapses to content width
Using a combination of box shadows to cover all four edges
Using relative positioning and a left offset to compensate for extra space taken up by the box shadow
Here's the minimal CSS that works:
h5{
display:inline;
font-size:1em;
line-height: 1.75em;
position: relative;
left: .5em;
background:#ff0000;
box-shadow: 0 -.25em 0 .5em #ff0000,
0 .25em 0 .5em #ff0000,
.25em 0 0 .25em #ff0000,
-.25em 0 0 .25em #ff0000;
}
And it looks like this on Chrome v41, OS X 10.10.3:
See fiddle here: https://jsfiddle.net/teddyrised/4f7jj7L8/2/

Related

Grow to fit available space, then eagerly wrap text instead of expanding parent [duplicate]

This question already has answers here:
How to match width of text to width of dynamically sized image/title?
(2 answers)
Closed 6 months ago.
Consider the example below, a tooltip or modal or something outside the normal layout flow where the container is able to grow, but should shrink to fit its contents. Here I would like the header (blue) to fit on a single line, (greedily) expanding the parent. That is, to behave like it normally would. But the "body text" (red) should only grow to fill the available space and then (eagerly) wrap the text.
Unfortunately there seems to be no obvious CSS property to express eagerly wrapping text layout.
article {
position: absolute;
background: whitesmoke;
padding: 1em 2em;
}
header {
color: blue;
}
main {
color: red;
}
<article>
<header>This should expand parent to fit.</header>
<main>This should use available space, but wrap instead of expanding parent.</main>
</article>
PS: This might just be my search or problem description skills being terrible, but I've not found anything even describing this problem, let alone the solution. Please link anything you can find that is related to this.
One trick I stumbled upon is to set min-width: 100% and then set max-width to the actual minimum value.
min-width: 100%;
max-width: min-content; /* or even 0 */
You might think that setting max-width: 100% as well would work. Nope! I'm guessing because 100% then would refer to the width after its "preferred" width has been applied.
I would love a proper explanation of why this works, but my understanding of it is simply that max-width overrides the internal "preferred" width, and then min-width overrides max-width. Seeing as this is CSS, I'm sure it's much more complicated though :)
article {
position: absolute;
background: whitesmoke;
padding: 1em 2em;
}
header {
color: blue;
}
main {
color: red;
min-width: 100%;
max-width: min-content;
}
<article>
<header>This should expand parent to fit.</header>
<main>This should use available space, but wrap instead of expanding parent.</main>
</article>

For a CSS flexbox, allow an element to shrink to minimum(its intrinsic width, an absolute length) [duplicate]

This question already has answers here:
Flexbox children shrink up to a certain point (but don't expand if they don't need to)
(3 answers)
Closed 2 years ago.
I have a box that displays a number of text elements, one after the other along a row. I do not want the text within each element to wrap. Instead if there is insufficient room then it should truncate the text and show an ellipse.
This is easy to obtain using display: flex, and allowing each element to shrink to zero.
But the smaller elements are shrunk so much that the text almost completely disappears. In that case I would rather shrink the smaller elements a little less, and shrink the bigger elements more. This can also be obtained by giving each element a bigger min-width (say 100px) so that it cannot shrink beyond a certain point.
My problem occurs when one of the text element's intrinsic width is already smaller than 100px. Since I just specified that the element had a longer min width, the browser leaves extra space after the element. I don't want that extra space.
I would prefer not to use java-script. I'm seeking a solution using CSS. It's so close that I feel that it should be possible, but none of my attempts have come out correctly.
If I knew in advance which text elements were tiny, then I can specify that the tiny elements should not flex at all, and get the desired outcome. But without using javascript I do not know which elements are tiny.
If I could set the min-width of an element to the minimum of its intrinsic width and 100px then I would get the desired outcome. Although CSS does have a min() function which may be used for min-width, unfortunately it appears that I am not allowed to use max-content as an argument to that function.
The description of min-width on MDN (https://developer.mozilla.org/en-US/docs/Web/CSS/min-width) states that fit-content(100px) is a syntactically valid way of specifying the min-width. I hoped to craft an element whose minimal content size is zero, and max size is the text element's intrinsic length. Then fit-content(100px) would either be 100px, or if that's larger than the intrinsic length, the intrinsic length. But whenever I use fit-content() with an argument the browser says that the expression is invalid.
Finally I tried to use a grid display. But then fit-content() either uses the intrinsic width or 100px, but does not expand further. I tried experimenting with minmax with no luck (it seems I cannot put fit-content() as an argument to minmax()). Besides I do not know the number of text elements, but a grid display wants me to specify that number so I don't think a grid can be made to work.
So is there any way to obtain the desired outcome using just CSS. Since this text is for an Electron program, I only care about Chrome as a browser. I have an example below showing each of my attempts.
main {
display: flex;
flex-direction: column;
width: 600px;
border-style: solid;
border-color: black;
border-width: 1px;
}
span {
background-color: skyblue;
padding: 10px;
margin: 0 10px 10px 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex: 0 1 auto;
}
section {
width: 100%;
display: flex;
}
.set-min-width span {
min-width: 100px;
}
.set-min-width .no-shrink {
flex: none;
min-width: initial;
}
.use-max-expression span {
min-width: min(100px, max-content);
}
.use-fit-content div {
flex: 0 1 auto;
min-width: fit-content(100px);
display: flex;
}
.use-fit-content div span {
min-width: 0;
}
.use-grid {
display: grid;
grid-template-columns: fit-content(100px) fit-content(100px) fit-content(100px);
}
.use-grid span {
min-width: 0;
}
<main>
<h4>The hidden overflow allows each text item to shrink to nothing<br> Both small and medium elements are heavily truncated<br></h4>
<section>
<span>Medium length</span>
<span>Tiny</span>
<span>Longer text that has to be truncated to fit within the section. It is far far far far too big to fit.</span>
</section>
<h4>By setting a min-width to 100px I can control truncation of medium element. But the tiny element now has extra space after it that I do not want.</h4>
<section class="set-min-width">
<span>Medium length</span>
<span>Tiny</span>
<span>Longer text that has to be truncated to fit within the section. It is far far far far too big to fit.</span>
</section>
<h4>1: This is what I want.<br> But to obtain it I had to specify a different style for the tiny element so that it would not flex. But I don't know in advance which elements are tiny.</h4>
<section class="set-min-width">
<span>Medium length</span>
<span class="no-shrink">Tiny</span>
<span>Longer text that has to be truncated to fit within the section. It is far far far far too big to fit.</span>
</section>
<h4>2: I want the min-width to be the element's max-content if this is smaller than the truncation limit<br> `min(100px, max-content)` would return the correct result, but it is not valid CSS and so is ignored</h4>
<section class="use-max-expression">
<span>Medium length</span>
<span>Tiny</span>
<span>Longer text that has to be truncated to fit within the section. It is far far far far too big to fit.</span>
</section>
<h4>3: I tried to use fit-content(arg)<br> The inner element has min-width 0, so its min-content size should be 0<br> Its max-content size is the intrinsic width of the text<br> So the fit-content(100px) size should be 100px if that's smaller than intrinsic
width, or the intrinsic width otherwise.<br> Despite MDN stating that fit-content with argument is valid CSS for min-width, the browser rejects `fit-content(100px)`</h4>
<section class="use-fit-content">
<div><span>Medium length</span></div>
<div><span>Tiny</span></div>
<div><span>Longer text that has to be truncated to fit within the section. It is far far far far too big to fit.</span></div>
</section>
<h4>4: Using fit-content(100px) in a grid does not work either. The elements are indeed not expanded if natural width is smaller than 100px. But the elements do not flex to take rest of space.<br> Besides a grid won't work because the number of columns is
not known in advance.
</h4>
<section class="use-grid">
<span>Medium length</span>
<span>Tiny</span>
<span>Longer text that has to be truncated to fit within the section. It is far far far far too big to fit.</span>
</section>
</main>
I was giving this a little play around and this was the best I could come up width. The problem that I can see is that flex-box is terrible for not obeying boxes without setting its inner elements with a max-width of some kind. There is a few other answers I found on Stackoverflow but none of them seem to really fix what you are asking for. In my answer I used max-width on the spans, along with text-overflow: ellipsis and overflow: hidden to create something close to what you had requested.
code below and codepen - here https://codepen.io/rl4444/pen/oNLQVRr?editors=1100
HTML
<main>
<h4>my solution</h4>
<section class="box-items">
<span>Medium length</span>
<span>Tiny</span>
<span>Longer text that has to be truncated to fit within the section. It is far far far far too big to fit.</span>
</section>
</main>
CSS
main {
max-width: 600px;
width: 100%;
border-style: solid;
border-color: black;
border-width: 1px;
position: relative;
box-sizing: border-box;
}
span {
background-color: skyblue;
padding: 10px;
margin: 0 10px 10px 0;
white-space: nowrap;
text-overflow: ellipsis;
max-width: 300px;
}
section {
display: flex;
box-sizing: border-box;
max-width: 100%;
}
.box-items span {
border: 1px solid red;
overflow: hidden;
}

Vertical alignment based on x-height

When trying to center content in a container CSS-Tricks has a great guide. However when trying to vertically center some text that's just slightly smaller than its container, I think a different way of vertically centering text might be preferable. Instead of using the entire height of the font, I would rather center it based on the x-height of the font (basically the height of a lowercase x)
And see this example where red is based on the entire height and green is based on the x-height
The only option I could come up with is to add a pseudo element to the text with the same height as the container and to use vertical-align: middle to it.
.pseudo {
white-space: nowrap;
}
.pseudo:before {
content: '';
display: inline-block;
vertical-align: middle;
height: 100px;
width: 0;
}
This works, but unfortunately only for a single line. I was wondering if anyone else tried to solve this issue and if perhaps there are best practices to follow? I am especially interested using as little "magic" numbers as possible and if there is a good solution for the multi line variant.
See Codepen for an example on why I want to center it based on the x-height, and my solution.
The differece between text center position and the small letters center is equal to (ascender height - x-height - descender height)/2 (basically we need to increase somehow the descender height to make it equal to ascender height - x-height to move the geometric center of the line box to the position of the small letters center). From these 3 unknowns, only x-height is available for CSS (via ex unit). Other font metrics can't be read and remain kind of 'magical numbers', so it's possible only to choose the a specific value for each specific font. But with this 'font-specific magic number' you can center any number of lines - by giving the inner element display:inline-block and assigning the magic value to its padding-bottom.
It seems impossible to get the needed value from the font metrics in pure CSS. Such vertical-align values as text-top/text-bottom can give the position of ascender or descender, but only one of them, exotic values like sub seem to be completely arbitrary, and I found no possibility to 'measure' the difference between two font metrics for one element.
My most successful attempt was the way to move the line (or lines) by half of the needed difference, making 'hybrid' centering (neither caps nor lowercase letters are centerd precisely, but 'optically' the text may look even better centered). This can be done by another pseudo element added to the last line, that has the height of the line box, but its aligned with the center of small letters:
.blue:after {
content: ':'; /* must contain text to get the auto height of the line box */
display: inline-block;
vertical-align: middle;
width: 0; /* making pseudo elenent invisible */
overflow: hidden;
}
Edited CodePen example with the result (I didn't hide pseudo elements there for visualization).
For centering the inline-block itself, any approach can be used, I choose the approach with second helper pseudo element that always has 100% height of the container, so no more magic numbers are needed.
Hope it helps:)
Sorry can't comment.
How about this:
.green {
color: #6c6;
background-color: #cfc;
vertical-align: -16%;
line-height: 60px;
}
http://jsfiddle.net/hcn25psh/3/
and some info which might help:
http://www.w3schools.com/cssref/pr_pos_vertical-align.asp
I think the only way to get the wanted result is to use Dinamyc CSS (DCSS).
First, you will need to create a function in your website that will retrieve the height of the text (to lower case).
Second, you will need to output the css with a static position which is in reality dynamic since it is printed by your dynamic code.
Here an example pasted from this link http://www.phpsnaps.com/snaps/view/get-text-height-width/ on how to retrieve your text height in PHP :
define("F_SIZE", 8);
define("F_FONT", "arial.ttf");
function get_bbox($text){
return imagettfbbox(F_SIZE, 0, F_FONT, $text);
}
function text_height ($text) {
$box = get_bbox($text);
$height = $box[3] - $box[5];
return $height;
}
function text_width ($text) {
$box = get_bbox($text);
$width = $box[4] - $box[6];
return $width;
}
And then you would echo your (x)HTML with CSS somehow like that :
echo "<span style=\"YourStyleProperty=" . **Your line height / 2 + your text height / 2 (Hint: use the PHP or equivalent if other language)** . ";\"
For more information on the imagettfbbox function :http://php.net/manual/fr/function.imagettfbbox.php
Feel free to post if you are having trouble finalizing the code, I will be glad to help if you show some efforts :).
For more info on DCSS and maybe better ideas/example don't hesitate to google DCSS.
Use this css
.outer {
display: table;
width: 200px;
height: 200px;
background-color:blue;
}
.inner {
display: table-cell;
vertical-align: middle;
/*You can align center if you want as well*/
/*text-align:center;*/
}
.box {
position: relative;
display: inline-block;
background: orange;
color: black;
font-size: 60px;
background-color: yellow;
}
And use this markup. So whatever is in the "box" div will be centered.
So it is just about adding an outer to act as table and inner to act as cell so you can use the vertical align middle for multiple lines.
<div class="outer">
<div class="inner">
<div class="box">
Texty</br>
Texty
</div>
</div>
</div>
here is a codepen
Ilya Streltsyn's answer (the accepted one) is amazing. Exactly the kind of thing I like about CSS - if you know the mechanics well enough, you can accomplish anything.
I have generalized the accepted answer into a reusable class, tested on Chrome, Firefox and Edge. I have also fixed issues with how it sits in the document flow (the container would carry extra width from the ::after elements, and would appear lower than sibling elements). You can easily use the class as follows:
<any class="x-height">
<span class="x-height">
Centered text
</span>
</any>
And below is the Sass source (Also check it out on Codepen):
%x-height {
content: 'x';
display: inline-block;
vertical-align: middle;
width: 0;
}
.x-height {
vertical-align: bottom;
&::after {
#extend %x-height;
content: '';
height: 100%;
}
> .x-height {
display: inline-block;
vertical-align: baseline;
margin-left: -1ch;
margin-right: calc(-1ch / 2);
white-space: nowrap;
#at-root {
#-moz-document url-prefix() {
& {
margin-left: auto;
margin-right: auto;
&::before { display: none; }
}
}
}
&::before, &::after {
#extend %x-height;
visibility: hidden;
}
&::after {
width: 1ch;
margin: 0 -1ch;
}
}
}
You probably need one of these jQuery plugins:
FlowType: Web typography at its finest: font-size and line-height based on element width
Squishy: A plugin for fitting heading text to its container
Responsive Text: Set font sizes responsively based on its’ container width
TypeButter: Allows you to set optical kerning for any font on your website
FitText: FitText makes font-sizes flexible
SlabText: A jQuery plugin for producing big, bold and responsive headlines
Auto Line-Height: A jQuery plugin for flexible layouts

How should I set this caption over my image using CSS?

I am beginning to dabble in responsive design, and as a result am trying to pinpoint best practices in my CSS. I am working on placing caption text over what will eventually be a custom, jquery driven image slider.
All of this is running at http://www.taylorp0994.net/websites/cincoschool/index.html, so please look to the live results and code for further information.
I have achieved what appears to be a workable solution; however, I fear it is not semantic to use pixels, regardless of context. How can I use percentages to achieve the same look and what approach should I take, (margin-top, position:relative/top, etc.)? I've yet to have much success with any of the obvious except for my current solution which is to position:relative the caption box and move it up via top:-46.5px.
The work you've posted looks really promising!
Two things worth mentioning from my cursory look:
There's nothing particularly 'wrong' with using pixel measurements. The only time this will potentially become a problem for you is with the caption's length. If the text is likely to change length (and thus: roll onto two lines), then using a set height adjustment won't work.
There's a lot of empty 'p' tags within the caption, is that deliberate?
The way I tend to tackle this type of task is to use positioning:
Have a single div wrap that contains both the image and the caption. Position this relative;
Set the image z-index to a low number;
Set the caption's z-index higher, and set to position: absolute, bottom: 0. This will position the caption off the bottom edge of the parent div, which will in turn inherit it's height from the image.
Two secs and I'll post an example.
Here you go: http://jsfiddle.net/HhuhR/ This is very quick-and-dirty but should help put you on the right track:
<style>
.img-wrap{
width: 60%; /*just here for the preview */
position: relative;
}
.img-wrap img{
max-width: 100%;
z-index: 1
}
.img-wrap .caption{
display: block;
width: 100%;
position: absolute;
bottom: 5px; /*if using padding in the caption, match here */
left: 0;
z-index: 2;
margin: 0;
padding: 5px 0;
text-indent: 5px;
color: #fff;
font-weight: bold;
background: rgba(0, 0, 0, 0.4);
}
</style>
<div class="img-wrap">
<img src="http://taylorp0994.net/websites/cincoschool/img/slide1.png" alt= "">
<span class="caption">Lorem ipsum dolor sit amet</span>
</div>
Do remember that as your viewport width gets narrower, the caption text will dominate the image (as the image itself gets smaller). I tend to identify a point in my design where this becomes a problem and simply overwrite the caption position for bottom/left and set position: relative - this drops the caption directly beneath the image rather than over-lapping (and potentially fully covering) it.

DIV between two floated images isn't sizing properly

I need to create a dialog box using custom images created by a designer. For purposes of this discussion, this the correct answer for my application. The dialog box must be able to withstand changes in width and height. This is easy to do with a table, but I want to maintain a table-less design, so I figured that I could do this using 3 rows of DIV's. For example, float an image to the left, float an image to the right, and put a DIV in between then with the image set to the background so that text can be entered over it.
Here is demo of my failed attempt to do this: (just one row shown)
http://www.seaburydesign.com/rounded/demo.html
As you can see, this almost working. But the DIV in the middle is only the size of the content inside of it, even though I have set the height and width. I need to keep the width flexible.
Any ideas on how to fix this?
Remove the following line:
display:inline;
Besides being useless in this case (the inline behavior is already working because of the floats) "inline" property doesn't allow you to set the element's width or height. For a clearer understanding, read w3c's article.
If you make the rounded corners of your images white instead of transparent, you can apply the background-image to the header-tag instead of the middle div. This will create the impression that the middle div has the same height as both images.
Update
If possible (depending on what browsers you need to support), you could do rounded corners with CSS3's border-radius property, instead of using images. That would be something like:
header {
-webkit-border-top-left-radius: 10px;
-webkit-border-top-right-radius: 10px;
-moz-border-radius-topleft: 10px;
-moz-border-radius-topright: 10px;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
}
You could also try this border-radius CSS-generator to find the properties that suit you best.
The css display: inline in your container div's voids any setting for width. Use display: block; float: left; margin: 0 XXpx; for your div (with XX being the width of the images on the sides).
Edit:
Concretely this would be:
div#yourdiv {
background-image: url("images/module_header_bg.jpg");
color: white;
display: block;
float: left;
font-weight: bold;
height: 42px;
width: auto;
}
and both img tags
img {
float: left;
}
This creates a dynamic sized box for your content, or you set width of the div to a specific value like width: 300px instead of width: auto.

Resources