I have setup a list of tags inside an unordered list. I want this component to house a vertically aligned items on the side of the page.
It currently looks like below:
Here's how the sidebar is currently setup:
export default class SideBar extends PureComponent {
render() {
return (
<div className="sidebar-container">
<ul class="sidebar-list">
<li role="presentation" class="active">Home</li>
<li role="presentation">Profile</li>
<li role="presentation">Messages</li>
</ul>
</div>
);
}
}
with css
.sidebar-container {
position: absolute;
left: 0;
top: 0;
bottom: 0;
flex-shrink: 1;
z-index: 999;
// width: 20px;
}
.sidebar-container > ul {
list-style: none;
}
.sidebar-list > li {
-webkit-transform: rotate(180deg);
transform: rotate(180deg);
-webkit-writing-mode: vertical-lr;
-ms-writing-mode: tb-lr;
writing-mode: vertical-lr;
// background-color: pink;
color: white;
}
.sidebar-list > li > a {
color: red;
text-decoration: none;
text-transform: uppercase;
margin-top: 16px;
background-color: green;
font-size: 5vh;
}
How can I stick it to the left?
Browsers give uls a default padding (Chrome for example gives it 40px). You need to set that to 0 like this:
.sidebar-container > ul {
list-style: none;
padding: 0;
}
If I'm understanding correctly, couldn't you just used fixed?
.sidebar-container {
position: fixed;
top: 0;
left: 0;
}
It will be removed from the flex flow (I believe) so you might need to adjust some other bits and bobs based on that.
ul has default padding you need to override it.
See here. http://jsfiddle.net/bhupendra_nitt/1dxt36sb/6/
If you have not got a browser reset in your project you should do. Use normalize
https://necolas.github.io/normalize.css/ or something similar. Most of these issues will not be present for you after but I as everyone has said you need to remove the default padding from the ul
Related
This question expands upon 'Separators For Navigation' by asking, how it is possible to remove the separators at the line breaks cause by viewport size.
Wide Viewport
-> Item 1 | Item 2 | Item 3 | Item 4 | Item 5 <-
Small Viewport
-> Item 1 | Item 2 | Item 3 <-
-> Item 4 | Item 5 <-
Here is a fiddle that shows how a pipe remains at the line break:
Fiddle.
I'm interested in a css-only solution, but javascript is acceptable if it provides the only possible solution.
Explanation
You can exploit fact that trailing and line trailing white space automatically collapses:
document.write(
'word<b style="background: red; outline: 1px solid blue;"> </b>'
.repeat(42)
);
As you can see there are red spaces with blue outlines between words, but the very last and and two at line ends lack the red area because it's width collapsed to zero: that is the white-space collapsing in effect.
It is possible to adjust width with word-spacing and use pseudo element instead, so setting inline ::after { content: ' '; word-spacing: 2em; } gives you wide inline rectangle that can have decorated backgrounds or borders but disappears when it is not between words.
Simplified example
Simplified use case (from https://codepen.io/myf/pen/dyOzpZM, tested just in 2021-02 evergreen Firefox and Chromium, will not work in pre-Chromium Edge; for more robust example see the second snippet below):
ul {
text-align: center;
padding: 0;
}
li {
display: inline;
}
li::after {
/*
This has to be space, tab or other
breakable white-space character:
*/
content: " ";
word-spacing: 1em;
background-image: linear-gradient(
-0.2turn,
transparent 0 calc(50% - 0.03em),
currentcolor 0 calc(50% + 0.03em),
transparent 0
);
}
/*
That's it: just inline text
with styled ::after spaces
that collapse at line breaks
and at the end of the element.
That's basically how spaces work in text.
*/
/*
Unrelated whimsical effects:
*/
body { background: #456; color: #fed; min-height: 100vh; margin: 0; display: flex; align-items: center; }
ul { --dur: 3s; font-family: Georgia, serif; font-size: min(7vw, calc(100vh / 7)); margin: 0 auto; position: relative; padding: 0 1em; -webkit-text-fill-color: #999; text-transform: capitalize; animation: poing var(--dur) infinite alternate ease-in-out; }
#keyframes poing { from { max-width: 3.4em; } to { max-width: min(19em, calc(100vw - 2em)); color: lime; } }
ul::before, ul::after { -webkit-text-fill-color: currentcolor; position: absolute; top: 50%; transform: translatey(-50%); animation: calc(var(--dur) * 2) calc(var(--dur) * -1.5) infinite forwards linear; }
ul::before { content: "☜"; left: 0; animation-name: a !important; }
ul::after { content: "☞"; right: 0; animation-name: b !important; }
#keyframes a { 50% { content: "☛"; } }
#keyframes b { 50% { content: "☚"; } }
ul:hover, ul:hover::before, ul:hover::after { animation-play-state: paused; }
<ul>
<li>foo</li>
<li>bar</li>
<li>baz</li>
<li>gazonk</li>
<li>qux</li>
<li>quux</li>
</ul>
It uses flat list with single word items, so is not very relevant for real-world usage.
More realistic example with elements highlights
nav {
text-align: center;
padding-right: 1em; /* = li::after#word-spacing */
}
ul {
display: inline;
margin: 0;
padding: 0;
}
li {
display: inline;
/*
white-space: nowrap should be moved to child A
because IE fails to wrap resulting list completely
*/
}
li::before {
content: ' ';
/*
this content is important only for Chrome in case
the HTML will be minified with *no whitespaces* between </li><li>
*/
}
li::after {
content: ' ';
/*
this is actual placeholder for background-image
and it really must be space (or tab)
*/
white-space: normal;
word-spacing: 1em;
/*
= nav#padding-right - this actually makes width
*/
background-image: radial-gradient(circle, black, black 7%, transparent 15%, transparent 35%, black 45%, black 48%, transparent 55%);
background-size: 1em 1em;
background-repeat: no-repeat;
background-position: center center;
opacity: 0.5;
}
/*
no need to unset content of li:last-child::after
because last (trailing) space collapses anyway
*/
a {
white-space: nowrap;
display: inline-block; /* for padding */
padding: 1em;
text-decoration: none;
color: black;
transition-property: background-color;
transition-duration: 500ms;
}
a:hover {
background-color: #ccc;
}
/*
For demonstrative purposes only
Give items some content and uneven width
*/
nav:hover > ul > li {
outline: 3px dotted rgba(0,0,255,.5);
outline-offset: -3px;
}
nav:hover > ul > li::after {
opacity: 1;
background-color: rgba(255, 0, 0, .5);
}
nav:hover > ul > li:hover {
outline-style: solid;
}
nav:hover > ul > li:hover::after {
background-color: cyan;
}
nav:hover > ul > li > a {
outline: 3px solid rgba(0,255,0,.5);
outline-offset: -3px;
}
nav > ul {
counter-reset: c;
}
nav > ul > li {
counter-increment: c;
}
nav > ul > li > a::before {
content: counter(c, upper-roman) '. ';
letter-spacing: .3em;
}
nav > ul > li > a::after {
content: ' item ' counter(c, lower-roman);
word-spacing: .3em;
letter-spacing: .1em;
transform: translatex(.1em);
display: inline-block;
}
<nav>
<ul><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li>
</ul>
</nav>
<!-- For demonstrative purposes is content of links made by CSS
-->
(Originally from https://jsfiddle.net/vnudrsh6/7/) This proof-of-concept uses background-image of "eventually colapsing" CSS generated content space after each <li>. Tested in 2016 in Firefox, Chrome and IE11.
Obviously you might need to use some character or more complex shape as divider. Naturally you can use (vector) background-image, and you can even use text in SVG, although making it correspond with surrounding ("real") text might be quite daunting.
Bare-bones with SVG
Minimal working example without any "list" element, with textual ❦ fleuron:
body {
text-align: center;
}
b::after {
content: " ";
word-spacing: 16px;
background: url("data:image/svg+xml;charset=utf-8,\
<svg xmlns='http://www.w3.org/2000/svg' \
viewBox='-3,-15,16,16'>\
<text>❦</text>\
</svg>");
}
<b>foo</b> <b>bar</b> <b>baz</b> <b>gazonk</b> <b>qux</b> <b>quux</b>
<b>foo</b> <b>bar</b> <b>baz</b> <b>gazonk</b> <b>qux</b> <b>quux</b>
<b>foo</b> <b>bar</b> <b>baz</b> <b>gazonk</b> <b>qux</b> <b>quux</b>
Other notable answers:
Same technique used in overlooked Liphtier's answer from 2014. (I've found that one long after posting this answer, so to my disappointment I cannot claim my answer is was first.)
Same technique used in few months younger Tom Robinson's answer.
gfullam's answer using flex-box, very impressive alternative with plain over-extending borders and different spacing due flex arrangement.
Oriol's answer for left-aligned list using overflow hidden and real character in pseudo.
A different solution from that same CSS: Last element on line seems like it would work here.
HTML:
<div>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
</ul>
</div>
CSS:
div {overflow: hidden; margin: 1em; }
div ul { list-style: none; padding: 0; margin-left: -4px; }
div ul li { display: inline; white-space: nowrap; }
div ul li:before { content: " | "; }
(Fiddle)
If you have static width of your element you can calculate by the media-screen.
If not use script
body {
text-align: center;
}
ul {
margin: 0;
padding: 0;
list-style: none;
}
li {
display: inline-block;
&:not(:last-child):after {
content: ' |';
}
}
#media screen and (max-width: 265px) {
li {
display: inline-block;
&:not(:last-child):after {
content: '';
}
}
}
Nice question. For the life of me, I can't think of a water-tight CSS-only solution I'm afraid...
I've modified an old solution to a similar question posted a while back: CSS: Last element on line. Funnily enough I was looking for a solution to another problem I had a while back and stumbled across this - been bookmarked since!
Here's a fiddle with my updates: https://jsfiddle.net/u2zyt3vw/1/
HTML:
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
</ul>
CSS:
body {
text-align: center;
}
ul {
margin: 0;
padding: 0;
list-style: none;
}
li {
display: inline-block;
&:not(:last-child):after {
content: ' |'
}
}
li.remove:after {
content: none;
}
jQuery:
$(window).on("resize", function () {
var lastElement = false;
$("ul > li").each(function() {
if (lastElement && lastElement.offset().top != $(this).offset().top) {
lastElement.addClass("remove");
}
lastElement = $(this);
}).last().addClass("remove");
}).resize();
NOTE - it works best onload at the moment, resizing causes a few issue even if I use toggleClass(). So keep pressing "Run" every time you resize the view. I'll work on it and get back to you..
My implementation with JavaScript: https://jsfiddle.net/u2zyt3vw/5/
Hit "Run" again after you've resized the window.
You can also add event listeners such as onresize. Here's the JS:
var listItems = document.getElementsByTagName("li");
var listItemsWidth = [];
var listItemsDistance = [];
for (let i = 0; i < listItems.length; i++) {
listItemsWidth[i] = listItems[i].offsetWidth;
listItemsDistance[i] = listItems[i].getBoundingClientRect().right;
}
for (let i = 0; i < listItems.length; i++) {
if (listItemsDistance[i] == Math.max.apply(null, listItemsDistance)) {
listItems[i].classList -= "notLast";
} else {
listItems[i].classList = "notLast";
}
}
I added the notLast class to all of your elements, and that's what contains the :after pseudo-element with the pipe. This script removes this class from the ones that are closer to the right edge of the container.
I also messed around with the :after pseudo-element and made it position:absolute; for dark reasons.
I've created CSS for anchor text that transitions a background-color and a border-bottom using opacity. (This is done to meet the Chrome Lighthouse auditor's spec on avoiding transitions except on compositor-only effects.)
The CSS puts the transitioned items on a ::before pseudo-element built on the anchor. Notice that the pseudo-element is absolutely positioned, which is a requirement of the technique as currently constructed. It works except when the anchor text follows a line break. A working, illustrated example is provided on this CodePen.
The CSS and HTML are also provided here:
* {
margin: 0;
padding: 0;
}
body {
margin: 1em;
font-size: 24px;
line-height: 1.5;
}
header,
article {
display: inline-block;
margin: 1em;
width: 100%;
}
p {
padding-bottom: 1em;
}
ul {
list-style: none;
}
li {
float: left;
margin-right: 1em;
}
a {
background-color: transparent;
border-bottom-color: transparent;
border-bottom-style: solid;
color: blue;
text-decoration: none;
position: relative;
}
a::before {
background-color: lightgray;
border-bottom-color: blue;
border-bottom-style: solid;
content: '';
opacity: 0;
transition: opacity 500ms ease;
position: absolute;
top: -5px;
left: 0;
bottom: -5px;
right: 0;
z-index: -1;
}
a:hover::before {
opacity: 1;
}
a::after {
--icon-width: 24px;
color: blue;
content: '';
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImJsdWUiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0iZmVhdGhlciBmZWF0aGVyLWV4dGVybmFsLWxpbmsiPjxwYXRoIGQ9Ik0xOCAxM3Y2YTIgMiAwIDAgMS0yIDJINWEyIDIgMCAwIDEtMi0yVjhhMiAyIDAgMCAxIDItMmg2Ij48L3BhdGg+PHBvbHlsaW5lIHBvaW50cz0iMTUgMyAyMSAzIDIxIDkiPjwvcG9seWxpbmU+PGxpbmUgeDE9IjEwIiB5MT0iMTQiIHgyPSIyMSIgeTI9IjMiPjwvbGluZT48L3N2Zz4=);
margin-left: 0.2em;
padding-left: var(--icon-width);
background-size: var(--icon-width) var(--icon-width);
background-position: center center;
background-repeat: no-repeat;
z-index: 999;
}
<body>
<header>
<ul>
<li><a href=#>Anchor 1</a></li>
<li><a href=#>Anchor 2</a></li>
<li><a href=#>Anchor 3</a></li>
</ul>
</header>
<article>
<h1>Composited transitions on border-bottom and background-color</h1>
<p>Avoid non-composited transitions! <code>opacity</code> and <code>transform</code> transitions are preferred by the Lighthouse auditor.</p>
<p>See: Stick to Compositor-Only Properties and Manage Layer Count by
Paul Lewis of Google.</p>
<p>Hover over the links to see the <code>border-bottom</code> and <code>background-color</code> fade in to cover the link text fully. This method works so long as the anchor text does not wrap across lines.</p>
<p><strong><em>Resize the window until the long link text on the line above this one wraps to a second line. At that point, the technique fails.</em></strong></p>
<p>Can you see a solution for the CSS?</p>
</article>
</body>
How would I edit the CSS so that the ::before element follows the line break of the anchor text?
Is it possible to reproduce this image using only CSS?
I want to apply this to my menu, so the brown background appears on hover instance
I don't know how to do this, I only have;
.menu li a:hover{
display:block;
background:#1a0000;
padding:6px 4px;
}
skew a parent element (li in this example) and inverse skew its child elements:
nav ul {
padding: 0;
display: flex;
list-style: none;
}
nav li {
transition: background 0.3s, color 0.3s;
transform: skew(20deg); /* SKEW */
}
nav li a {
display: block; /* block or inline-block is needed */
text-decoration: none;
padding: 5px 10px;
font: 30px/1 sans-serif;
transform: skew(-20deg); /* UNSKEW */
color: inherit;
}
nav li.active,
nav li:hover {
background: #000;
color: #fff;
}
<nav>
<ul>
<li>Home</li>
<li class="active">Products</li>
<li>Contact</li>
</ul>
</nav>
Here is a fiddle for use across different browsers - I created in a couple of minutes.
Try playing with the arguments, I used :before and :after to do this.
https://jsfiddle.net/DTBAE/
You can use the transform: skew(X, Y) property to achieve this. Creating a skewed outer container, then skew the opposite amount on an inner container to skew the text back to being straight. See this fiddle for example;
http://jsfiddle.net/UZ6HL/4/
From what you have said, I believe this is what you want, if not please clarify when the item should display the background.
.skew {
background: green;
color: #fff;
padding: 50px;
transform: skewX(-7deg);
font-size: 20px;
font-weight: 700;
}
.skew p {
transform: skewX(7deg);
}
<div class="skew">
<p>This is caption</p>
</div>
Here's an example
To have IE support just add -ms-transform: skew(20deg, 0deg); beside all the other transform: skew(20deg, 0deg);s.
NOTE: SPAN is NOT affected by transform CSS functionality, so you will need a DIV or change span to display: block; otherwise they will NOT be affected.
So just put the TEXT inside a separate div and unskew it.
example wrapper div is:
transform: skewx(35deg)
but text div is:
transform: skewx(-35deg);
here is codepen: https://codepen.io/dmitrisan/pen/NWaYEzV
You can use clip-path to make results like these.
For example:
* {
box-sizing: border-box;
}
body {
padding: 0;
margin: 0;
}
ul {
display: flex;
width: 100%;
flex-direction: row;
gap: 20px;
background: #000;
padding: 0 10px;
justify-content: flex-end;
}
li {
list-style-type: none;
clip-path: polygon(20% 0%, 100% 0, 80% 100%, 0% 100%);
background: blue;
padding: 10px 50px;
}
a {
color: #fff;
}
<ul>
<li>Home</li>
<li>About</li>
</ul>
You can generate your clip from here and use it in your code.
Here is a working Fiddle for reference
Is it possible to reproduce this image using only CSS?
I want to apply this to my menu, so the brown background appears on hover instance
I don't know how to do this, I only have;
.menu li a:hover{
display:block;
background:#1a0000;
padding:6px 4px;
}
skew a parent element (li in this example) and inverse skew its child elements:
nav ul {
padding: 0;
display: flex;
list-style: none;
}
nav li {
transition: background 0.3s, color 0.3s;
transform: skew(20deg); /* SKEW */
}
nav li a {
display: block; /* block or inline-block is needed */
text-decoration: none;
padding: 5px 10px;
font: 30px/1 sans-serif;
transform: skew(-20deg); /* UNSKEW */
color: inherit;
}
nav li.active,
nav li:hover {
background: #000;
color: #fff;
}
<nav>
<ul>
<li>Home</li>
<li class="active">Products</li>
<li>Contact</li>
</ul>
</nav>
Here is a fiddle for use across different browsers - I created in a couple of minutes.
Try playing with the arguments, I used :before and :after to do this.
https://jsfiddle.net/DTBAE/
You can use the transform: skew(X, Y) property to achieve this. Creating a skewed outer container, then skew the opposite amount on an inner container to skew the text back to being straight. See this fiddle for example;
http://jsfiddle.net/UZ6HL/4/
From what you have said, I believe this is what you want, if not please clarify when the item should display the background.
.skew {
background: green;
color: #fff;
padding: 50px;
transform: skewX(-7deg);
font-size: 20px;
font-weight: 700;
}
.skew p {
transform: skewX(7deg);
}
<div class="skew">
<p>This is caption</p>
</div>
Here's an example
To have IE support just add -ms-transform: skew(20deg, 0deg); beside all the other transform: skew(20deg, 0deg);s.
NOTE: SPAN is NOT affected by transform CSS functionality, so you will need a DIV or change span to display: block; otherwise they will NOT be affected.
So just put the TEXT inside a separate div and unskew it.
example wrapper div is:
transform: skewx(35deg)
but text div is:
transform: skewx(-35deg);
here is codepen: https://codepen.io/dmitrisan/pen/NWaYEzV
You can use clip-path to make results like these.
For example:
* {
box-sizing: border-box;
}
body {
padding: 0;
margin: 0;
}
ul {
display: flex;
width: 100%;
flex-direction: row;
gap: 20px;
background: #000;
padding: 0 10px;
justify-content: flex-end;
}
li {
list-style-type: none;
clip-path: polygon(20% 0%, 100% 0, 80% 100%, 0% 100%);
background: blue;
padding: 10px 50px;
}
a {
color: #fff;
}
<ul>
<li>Home</li>
<li>About</li>
</ul>
You can generate your clip from here and use it in your code.
Here is a working Fiddle for reference
I have the following case generated by a plugin which I am not able to rewrite as I would need to fix this. It generates breadcrumbs for a website like the following example:
<li><a>Parent</a></li>
<li><a>Subparent</a></li>
<li><a>Subsubparent</a></li>
<li>Current Site</li>
I have styled the links to be clickable more easy
li {height: 40px;}
li a {padding: 5px; display: inline-block; height: 30px;}
Now of course the last element does not get the same padding and looks wired. I am not able to wrap a html element like span around it in php.
Is there a css selector to select the text inside of an element, without affecting the element itself? Or wraps an html element like span around it, something like
li:last-child::before {content:"<span>"}
Every hint appreciated! If someone likes jsfiddle here is one to play with.
Why don't you just add the padding to the last li together with the anchors?
Updated JsFiddle
li a, li:last-child {padding: 10px; display: inline-block;}
I have created the following work-around to get the right styling.
li:last-child::before {
content: "";
opacity: 0;
display: inline-block;
margin: 10px 0 10px 10px;
}
li:last-child::after {
content: ".";
opacity: 0;
display: inline-block;
margin: 10px 10px 10px 0;
}
Sadly one of the both pseudo elements needs content:"." or another real content. This is a solution to target spacing (margin/padding) without changing some css/html.
Updated jsfiddle
Still I would love to see clean css solutions!
My own suggestion, at its simplest, would be:
li {
height: 40px;
/* vertically-aligns the text to
the same 'line': */
line-height: 40px;
/* to display in a row, given the
text-alignment I assume this is required,
remove/adjust if not: */
float: left;
/* removes the bullets: */
list-style-type: none;
/* Just to clearly show the size of
the <li> elements: */
background-color: #ffa;
}
li:nth-child(odd) {
/* again, just to show the size: */
background-color: #f90;
}
li a {
/* to fill the whole of the <li>: */
display: block;
}
li a:hover {
/* just to show the hover 'hit-area': */
background-color: #f00;
transition: background-color 0.4s linear;
}
li {
height: 40px;
line-height: 40px;
float: left;
list-style-type: none;
background-color: #ffa;
}
li:nth-child(odd) {
background-color: #f90;
}
li a {
display: block;
}
li a:hover {
background-color: #f00;
transition: background-color 0.4s linear;
}
<ul>
<li>Parent
</li>
<li>Subparent
</li>
<li>Subsubparent
</li>
<li>Current Site</li>
</ul>
Now, to style them as 'breadcrumbs,' you could use generated content to provide
the separators, for example, you can update the CSS:
li {
/* other rules remain the same,
this is added to provide space
for the generated content: */
margin-left: 1em;
}
/* there is (usually) no marker before
the first breadcrumb, this removes its
space: */
li:first-child {
margin-left: 0;
}
/* other rules that, remain the
same, are excised for brevity */
li::before {
/* unicode for the guillemot,
'»', character: */
content: '\00BB';
/* to position easily and prevent
altering the position of the child
<a> elements: */
position: absolute;
/* simple means to move the generated
content off the left edge: */
right: 100%;
width: 1em;
text-align: center;
}
/* no marker the first breadcrumb: */
li:first-child::before {
/* removing both content and width: */
content: '';
width: 0;
li {
height: 40px;
line-height: 40px;
position: relative;
margin-left: 1em;
float: left;
list-style-type: none;
}
li:first-child {
margin-left: 0;
}
li a {
display: block;
}
li a:hover {
background-color: #f00;
transition: background-color 0.4s linear;
}
li::before {
content: '\00BB';
position: absolute;
right: 100%;
width: 1em;
text-align: center;
}
li:first-child::before {
content: '';
width: 0;
}
<ul>
<li>Parent
</li>
<li>Subparent
</li>
<li>Subsubparent
</li>
<li>Current Site</li>
</ul>