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?
Related
I have an animated line under links. It works fine for single line links but I have some links that are separated with line breaks <br>
Is there a way to have the animated underline come out along all the lines of the link?
Thanks
body {
padding: 20px;
font-family: Helvetica;
}
a {
font-weight: bold;
color: black;
position: relative;
text-decoration: none;
}
a::before {
content: "";
position: absolute;
width: 0%;
height: 2px;
bottom: 0;
background-color: #000;
transition: all 0.2s;
}
a:hover::before {
width: 100%;
}
my link
<br><br>
this is<br>a much<br>longer link
Use gradient like below:
body {
padding: 20px;
font-family: Helvetica;
}
a {
font-weight: bold;
color: black;
position: relative;
text-decoration: none;
background:linear-gradient(#000,#000) left bottom no-repeat;
background-size:0% 2px;
transition: all 0.5s;
}
a:hover {
background-size:100% 2px;
}
/* this will give another kind of animation (all lines will animate at the same time)*/
.alt {
-webkit-box-decoration-break:clone;
box-decoration-break:clone;
}
my link
<br><br>
this is<br>a much<br>longer link
<br><br>
<a class="alt" href="">this is<br>a much<br>longer link</a>
Related:
How to animate underline from left to right?
How can I achieve a CSS text loading animation over multiple lines?
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>
I'm a little frustrated with the positioning of a CSS triangle on the :after pseudo-element which appears correctly in Chrome and Safari but appears to have a little extra padding or margin in Firefox.
First, screenshots:
The top screen is how "Choose a Subject" appears in Firefox, the bottom how it appears in Chrome and Safari. Obviously this is purely cosmetic, but I'm going for that chevron look.
Here is the markup (for the first item, the darker-blue item is another li):
<nav class="menu pill-menu inline" role="navigation">
<!-- Major Category
======================
--> <ul>
<li class="has-subnav primary">
<input type="checkbox" id="primary-menu" class="checkbox-toggle"/>
<label class="label" for="primary-menu">
Choose a Subject
</label>
</li>
</ul>
</nav>
Here is the Sass:
.pill-menu {
& > ul > li { display: inline-block; }
.primary > .label,
.secondary > .label {
#include css-transition(all, .2s, ease-out);
cursor: pointer;
padding: .5em;
position: relative;
}
.primary > .label {
border-radius: 5px 0 0 5px;
}
.primary > .label:after {
border-top: 18px solid transparent;
border-bottom: 18px solid transparent;
border-left: 18px solid $light-blue-object; // TODO: Abstract This
content: "";
height: 0;
right: -1em;
top: 0;
position: absolute;
width: 0;
z-index: 1;
}
}
I've tried several different things: using an :after pseudo-element, I used as I am now right: -1em; I've also scrapped than and tried left: 100%; I set the triangle to a :before element and tried an assortment there. I'm stumped.
I tried changing up the fonts, hardcoding the font-size in pixels, mucking with the padding and the margin: nothing I can think of has any effect on that little space.
I appreciate your thoughts.
In case it matters, I am using normalize.css.
Additionally, I would like to add that both Chrome Dev Tools and Firefox Inspector [or whatever they call it ...] show no difference in inherited, computed, or generated styles. They appear the same in every way.
I'm just stumped. Let me know if there's other information I can provide.
Update
At your request, the compiled CSS:
.pill-menu .primary > .label:after {
border-bottom: 18px solid transparent;
border-left: 18px solid #50afdf;
border-top: 18px solid transparent;
content: "";
height: 0;
right: -1em;
position: absolute;
top: 0;
width: 0;
z-index: 1;
}
.pill-menu .primary > label {
background-color: #50afdf;
border-radius: 5px 0 0 5px;
cursor: pointer;
padding: .5em;
position: relative;
transition: all .2s ease-out;
}
It's not margins or paddings, but whitespace. Just remove the unnecessary spaces/newlines/tabs inside your label.
http://cssdeck.com/labs/v2f8qedp
<!-- Major Category
======================
--> <ul>
<li class="has-subnav primary">
<input type="checkbox" id="primary-menu" class="checkbox-toggle"/>
<label class="label" for="primary-menu"> Choose a Subject</label>
</li>
</ul>
</nav>
Tested this out on jsfiddle. Seems to solve the issue. Can you try at your end?
#-moz-document url-prefix() {
.pill-menu .primary > .label:after {
right: -.9em;
}
}
I have some CSS questions.
I have a li element with a certain width. I also want a :after
element for this li and use this as a tooltip. Is it possible to
give the tooltip an auto width and center it above the li?
If I have the tooltip in the li:after element working I still need a
little triangle arrow for it therefor I would need to apply a :after
element on a :after element. Is this possible?
If the order is triangle, followed by tooltip text, you can achieve this by using :before and :after (as suggested in the comment). The below example, also available on dabblet, should give you an idea.
http://dabblet.com/gist/4280779
HTML:
<ul>
<li>first - no tooltip</li>
<li data-tooltip="Tooltip second">Second</li>
<li data-tooltip="Tooltip third">third</li>
</li>
CSS:
li {
color: #900;
}
li:hover {
color: red;
position: relative;
}
li[data-tooltip]:hover:after {
content: attr(data-tooltip);
padding: 4px 6px 4px 20px;
color: yellow;
position: absolute;
left: 0;
top: -150%;
white-space: nowrap;
z-index: 20;
border-radius: 5px;
box-shadow: 0px 0px 2px #333;
background-color: black;
}
li[data-tooltip]:hover:before {
content: "\0394";
padding-left: 3px;
position: absolute;
left: 0px;
top: -150%;
color: yellow;
z-index: 21;
}
You can make the tooltip look nice by replacing background-color with background-image / gradient, etc.