:hover not working on svg when svg is in external file - css

So I'm learning SVG animation.
Basically all I'm trying to do is change the color of a circle when it's hovered over.
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 437.1 294.4" style="enable-background:new 0 0 437.1 294.4;" xml:space="preserve">
<style type="text/css">
.st0:hover {
fill: red;
}
</style>
<g id="Circle">
<path class="st0" d="M291.3,147.4c0,77-62.4,139.4-139.4,139.4S12.5,224.4,12.5,147.4C12.6,70.4,75,8,151.9,8
C228.9,8,291.3,70.4,291.3,147.4"/>
</g>
</svg>
This works exactly as expected when the svg code is inside the html file.
However, when I put it inside an svg file and call it in using the img tag the hover effect no longer works.
<img class="logo" src="url/logo.svg">
Is there a way to do this without embedding the svg code inside the html?
Thanks!

Can't be done with the <img> tag. See: Styling And Animating SVGs With CSS. Near the bottom of the page of this article there's a table with the pros and cons of each SVG embedding technique (ie, img, object, etc.). I have reproduced the table here:
| | CSS Interactions | CSS Animations | SVG Animations |
|:--------------------:|:----------------:|:--------------:|:--------------:|
| <img> | No | Yes* | Yes |
| CSS background image | No | Yes* | Yes |
| <object> | Yes* | Yes* | Yes |
| <iframe> | Yes* | Yes* | Yes |
| <embed> | Yes* | Yes* | Yes |
| <svg> (inline) | Yes | Yes | Yes |
*Only if inside <svg>

I am writing this answer as a sort of applied version of Sean's answer. The following snippet shows varying forms of how you can utilize <svg> and what their capabilities are in each context.
The main takeaway is that depending on the context, the <svg> may or may not receive style rules from the top level document and may or may not receive interactive events from the top level document (:hover).
I suggest you view the snippet below to see the applications in action:
const someSVG = `
<svg width="128" height="128" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<style>
.interactive.from-svg {
fill: red;
transition: fill 200ms ease;
}
.interactive.from-svg:hover {
fill: blue;
}
.animated.from-svg{
animation: 3s infinite alternate changecolor;
}
#keyframes changecolor{
from {
fill: green;
}
to {
fill: purple;
}
}
</style>
<circle class="interactive from-doc" cx="25" cy="25" r="25"></circle>
<circle class="animated from-doc" cx="75" cy="25" r="25"></circle>
<circle class="interactive from-svg" cx="25" cy="75" r="25"></circle>
<circle class="animated from-svg" cx="75" cy="75" r="25"></circle>
</svg>
`;
const dataUri = `data:image/svg+xml;base64,${btoa(someSVG)}`;
const imgContainer = document.getElementById("img-container"),
img = document.createElement("img");
imgContainer.appendChild(img);
img.src = dataUri;
const backgroundImageContainer = document.getElementById("background-image-container"),
backgroundImage = document.createElement("div");
backgroundImageContainer.appendChild(backgroundImage);
backgroundImage.style.width = "128px";
backgroundImage.style.height = "128px";
backgroundImage.style.backgroundImage = `url(${dataUri})`;
const iframeContainer = document.getElementById("iframe-container"),
iframe = document.createElement("iframe");
iframeContainer.appendChild(iframe);
iframe.src = dataUri;
main {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
section {
flex-basis: 50%;
flex-grow: 1;
flex-shrink: 0;
}
.interactive.from-doc {
fill: red;
transition: fill 200ms ease;
}
.interactive.from-doc:hover {
fill: blue;
}
.animated.from-doc {
animation: 3s infinite alternate changecolor;
}
#keyframes changecolor {
from {
fill: green;
}
to {
fill: purple;
}
}
li.pro {
color: green;
}
li.con {
color: red;
}
<h1><code><svg></code> Usage Comparison</h1>
<main>
<section id="external">
<h2><code><img></code></h2>
<div id="img-container"></div>
<pre><code><img src="some.svg"></code></pre>
<ul>
<li class="con">Does not receive <strong>any</strong> style from top level document</li>
<li class="con">Does not utilize CSS Interactions such as <code>:hover</code></li>
<li class="pro">Will use CSS Animations if they are defined within the svg element's style</li>
</ul>
</section>
<section>
<h2><code>background-image</code></h2>
<div id="background-image-container"></div>
<pre><code>
<style>
.rule{
background-image: url(some.svg);
}
</style>
<div class="rule"></div>
</code></pre>
<ul>
<li class="con">Does not receive <strong>any</strong> style from top level document</li>
<li class="con">Does not utilize CSS Interactions</li>
<li class="pro">Will use CSS Animations if they are defined within the svg element's style</li>
</ul>
</section>
<section>
<h2><code><iframe></code></h2>
<div id="iframe-container"></div>
<pre><code><iframe src="some.svg"></code></pre>
<ul>
<li class="con">Does not receive <strong>any</strong> style from top level document</li>
<li class="pro">Will use CSS Interactions if they are defined within the svg element's style</li>
<li class="pro">Will use CSS Animations if they are defined within the svg element's style</li>
</ul>
</section>
<section>
<h2>Inline <code><svg></code></h2>
<svg width="128" height="128" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<style>
.interactive.from-svg {
fill: red;
transition: fill 200ms ease;
}
.interactive.from-svg:hover {
fill: blue;
}
.animated.from-svg{
animation: 3s infinite alternate changecolor;
}
#keyframes changecolor{
from {
fill: green;
}
to {
fill: purple;
}
}
</style>
<circle class="interactive from-doc" cx="25" cy="25" r="25"></circle>
<circle class="animated from-doc" cx="75" cy="25" r="25"></circle>
<circle class="interactive from-svg" cx="25" cy="75" r="25"></circle>
<circle class="animated from-svg" cx="75" cy="75" r="25"></circle>
</svg>
<pre><code><svg>...</svg></code></pre>
<ul>
<li class="pro">Receives <strong>all</strong> style rules from top level document</li>
<li class="pro">Will use CSS Interactions if they are defined within the svg element's style</li>
<li class="pro">Will use CSS Animations if they are defined within the svg element's style</li>
</ul>
</section>
</main>
<img>
<img src="some.svg">
Does not receive any style from top level document
Does not utilize CSS Interactions such as :hover
Will use CSS Animations if they are defined within the svg element's style
background-image
<style>
.rule {
background-image: url(some.svg);
}
</style>
<div class="rule"></div>
Does not receive any style from top level document
Does not utilize CSS Interactions
Will use CSS Animations if they are defined within the svg element's style
<iframe>
<iframe src="some.svg"></iframe>
Does not receive any style from top level document
Will use CSS Interactions if they are defined within the svg element's style
Will use CSS Animations if they are defined within the svg element's style
inline <svg>
<svg>...</svg>
Receives all style rules from top level document
Will use CSS Interactions if they are defined within the svg element's style
Will use CSS Animations if they are defined within the svg element's style

Related

How to make an element of such a complex shape? [duplicate]

So I'm working on a site and I was wondering if it's possible to, purely using HTML5, CSS3 (and JavaScript if needed), make a div with a curved bottom, so it will look practically like this:
Or can this only be done using a background image?
<body>
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<ul class="nav">
<li>Home</li>
</ul>
</div>
</div>
</body>
There are different approaches that can be adopted to create this shape. Below is a detailed description of possibilities:
SVG Based Approaches:
SVG is the recommended way to create such shapes. It offers simplicity and scale-ability. Below are a couple of possible ways:
1- Using Path Element:
We can use SVG's path element to create this shape and fill it with some solid color, gradient or a pattern.
Only one attribute d is used to define shapes in path element. This attribute itself contains a number of short commands and few parameters that are necessary for those commands to work.
Below is the necessary code to create this shape:
<path d="M 0,0
L 0,40
Q 250,80 500,40
L 500,0
Z" />
Below is a brief description of path commands used in above code:
M command is used to define the starting point. It appears at the beginning and specify the point from where drawing should start.
L command is used to draw straight lines.
Q command is used to draw curves.
Z command is used to close the current path.
Output Image:
Working Demo:
svg {
width: 100%;
}
<svg width="500" height="80" viewBox="0 0 500 80" preserveAspectRatio="none">
<path d="M0,0 L0,40 Q250,80 500,40 L500,0 Z" fill="black" />
</svg>
2- Clipping:
Clipping means removing or hiding some parts of an element.
In this approach, we define a clipping region by using SVG's clipPath element and apply this to a rectangular element. Any area that is outside the clipping region will be hidden.
Below is the necessary code:
<defs>
<clipPath id="shape">
<path d="M0,0 L0,40 Q250,80 500,40 L500,0 Z" />
</clipPath>
</defs>
<rect x="0" y="0" width="500" height="80" fill="#000" clip-path="url(#shape)" />
Below is brief description of the elements used in above code:
defs element is used to define element / objects for later use in SVG document.
clipPath element is used to define a clipping region.
rect element is used to create rectangles.
clip-path attribute is used to link the clipping path created earlier.
Working Demo:
svg {
width: 100%;
}
<svg width="500" height="80" viewBox="0 0 500 80" preserveAspectRatio="none">
<defs>
<clipPath id="shape">
<path d="M0,0 L0,40 Q250,80 500,40 L500,0 Z" />
</clipPath>
</defs>
<rect x="0" y="0" width="500" height="80" fill="#000" clip-path="url(#shape)" />
</svg>
CSS Based Approaches:
1- Using Pseudo Element:
We can use ::before or ::after pseudo element to create this shape. Steps to create this are given below:
Create a layer with ::before OR ::after pseudo element having width and height more than its parent.
Add border-radius to create the rounded shape.
Add overflow: hidden on parent to hide the unnecessary part.
Required HTML:
All we need is a single div element possibly having some class like shape:
<div class="shape"></div>
Working Demo:
.shape {
position: relative;
overflow: hidden;
height: 80px;
}
.shape::before {
border-radius: 100%;
position: absolute;
background: black;
right: -200px;
left: -200px;
top: -200px;
content: '';
bottom: 0;
}
<div class="shape"></div>
2- Radial Gradient:
In this approach we will use CSS3's radial-gradient() function to draw this shape on the element as a background. However, this approach doesn't produce very sharp image and it might have some jagged corners.
Required HTML:
Only single div element with some class will be required i.e.
<div class="shape"></div>
Necessary CSS:
.shape {
background-image: radial-gradient(120% 120px at 50% -30px, #000 75%, transparent 75%);
}
Working Demo:
.shape {
background: radial-gradient(120% 120px at 50% -30px, #000 75%, transparent 75%) no-repeat;
height: 80px;
}
<div class="shape"></div>
JavaScript Based Approaches:
Although not required in this case but for the sake of completeness, I'm adding this approach as well. This can be useful in some cases as well:
HTML5 Canvas:
We can draw this shape on HTML5 Canvas element using path functions:
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(0, 40);
ctx.quadraticCurveTo(311, 80, 622, 40);
ctx.lineTo(622, 0);
ctx.fill();
<canvas id="canvas" width="622" height="80"></canvas>
Below is a brief description of the methods used in above code:
beginPath() is used to create a new path. Once a new path is created, future drawing commands are directed into the path.
moveTo(x, y) moves the pen to the coordinates specified by x and y.
lineTo(x, y) draws a straight line from the current pen position to the point specified by x and y.
quadraticCurveTo(cp1x, cp1y, x, y) draws a curve from current pen position to the point specified by x and y using control point specified by cp1x and cp1y.
fill() fills the current path using non-zero or even-odd winding rule.
Useful Resources:
Radial Gradient: Specs, MDN
SVG: Specs, MDN
HTML5 Canvas: Specs, MDN
CSS:
div{
background-color:black;
width:500px;
height:50px;
border-bottom-left-radius:50%;
border-bottom-right-radius:50%;
}
see is this ok for you
div {
background-color: black;
width: 500px;
height: 50px;
border-bottom-left-radius: 50%;
border-bottom-right-radius: 50%;
}
<div>
</div>
This is what you want:
div{
background-color: black;
width: 500px;
height: 300px;
border-radius: 0 0 50% 50% / 50px;
}
Unlike the accepted answer, this works even when the height of the div is increased.
Demo: jsFiddle
Yes, you can do this in CSS - basically make your div wider than the page to fix the too-rounded edges, then left-positioned to compensate, with bottom border radius using both x & y values, and negative bottom margin to compensate for the gap:
.round-bottom {
border-bottom-left-radius: 50% 200px; // across half & up 200px at left edge
border-bottom-right-radius: 50% 200px; // across half & up 200px at right edge
width: 160%; overflow: hidden; // make larger, hide side bits
margin-bottom: -50px; // apply negative margin to compensate for bottom gap
position: relative; left:-30%; // re-position whole element so extra is on each side (you may need to add display:block;)
}
.round-bottom {
border-bottom-left-radius: 50% 150px !important;
border-bottom-right-radius: 50% 150px !important;
position: relative;
overflow: hidden;
width: 160%;
margin-bottom:-50px;
left:-30%;
background-color:#444;
background-image: url('https://upload.wikimedia.org/wikipedia/commons/a/a2/Tropical_Forest_with_Monkeys_A10893.jpg'); background-position: center center;
background-size: 42% auto;
height:150px;
}
.container { width: 100%; height: height:100px; padding-bottom:50px; overflow:hidden;}
<div class="container"><div class="round-bottom"></div></div>
Try this
.navbar{
border-radius:50% 50% 0 0;
-webkit-border-radius:50% 50% 0 0;
background:#000;
min-height:100px;
}
jsFiddle File

How to display icon from svg sprite in pseudo element with ruby on rails

I want to create a menu like this Home > where the right arrow is an icon in an svg sprite. I would like to place the > as a psuedo element in the css selector so that i can make any menu have the right arrow just by adding the css selector .menu-arrow. I am trying to do this in ruby on rails 6 with no success.
I have tried adding the svg icon sprite as a background content but it doesn't seem to work correctly.
My svg sprite
path to svg sprite: app/assets/images/sprites.svg
<svg aria-hidden="true" style="position: absolute; width: 0; height: 0; overflow: hidden;" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<symbol id="icon-check-solid" viewBox="0 0 32 32">
<path d="M10.869 27.463l-10.4-10.4c-0.625-0.625-0.625-1.638 0-2.263l2.263-2.263c0.625-0.625 1.638-0.625 2.263 0l7.006 7.006 15.006-15.006c0.625-0.625 1.638-0.625 2.263 0l2.263 2.263c0.625 0.625 0.625 1.638 0 2.263l-18.4 18.4c-0.625 0.625-1.638 0.625-2.263-0z"></path>
</symbol>
<symbol id="icon-chevron-down-solid" viewBox="0 0 28 32">
<path d="M12.939 23.842l-12.146-12.146c-0.586-0.586-0.586-1.536 0-2.121l1.417-1.417c0.585-0.585 1.533-0.586 2.119-0.002l9.672 9.626 9.672-9.626c0.586-0.583 1.534-0.582 2.119 0.002l1.417 1.417c0.586 0.586 0.586 1.536 0 2.121l-12.146 12.146c-0.586 0.586-1.536 0.586-2.121 0z"></path>
</symbol>
</defs>
</svg>`
My css class
.menu-arrow {
&::after {
content: '';
display: inline-block;
height: 2rem;
width: 2rem;
background-image: asset-url("images/sprites.svg#icon-chevron-down-solid");
transform: rotate(90deg) scale(1.5);
fill: var(--color-white);
color: var(--color-white);
font-weight: 700;
border: 1px solid red;
}
}
}
How do i make this work?
This article suggests the #id in the CSS url is referencing a view rather than a symbol
You can certainly use your sprite as an inline SVG (shadow) DOM element via use, and make a helper to streamline the process (see this)
For your chevron specifically, you can achieve an almost identical graphic element using your pseudo element without a background (borders on two sides with rounded corners and rotated 45deg). I made a pen here that demonstrates this approach

How to control SVG filter attribute values using CSS?

I want to create animation in CSS, part of which I need to control the values of the SVG filter attributes.
Below, I try to call a CSS variable within the specularConstant attribute, and the browser returns an error.
Alternatively, is it possible to set the attribute using a selector in the CSS code, or is there any way to control an SVG attribute like this, via CSS?
Below a Reproducible Example:
#property --illumination-power {
syntax: '<number>';
initial-value: 1;
inherits: true
}
:root {
animation: my-animation 5s;
}
#keyframes my-animation {
0% {
--illumination-power: 0;
}
100% {
--illumination-power: 1.2;
}
}
body {
margin: 0;
background-color: black;
}
<svg width="100vw" height="100vh">
<defs>
<filter id="spotlight">
<feSpecularLighting specularConstant="var(--illumination-power)"
specularExponent="10" lighting-color="white">
<fePointLight x="200" y="100" z="70"/>
</feSpecularLighting>
</filter>
</defs>
<rect id="light-background" x="-500%" y="-500%" width="1000%" height="1000%" fill-opacity="0" filter="url(#spotlight)"/>
</svg>
You need to use the SVG <animate> element or JavaScript to do it.

CSS "filter" property does not work on Safari when using inline SVG in CSS like 'filter: url(data:image, ...)'

[EDIT] I've reviewed my entire post to add precisions about the problem and for additional ressources.
...
I'm new to SVG usage for webdesign, but I intend to implement a cool CSS feature for a theme. For information, I work with SASS to compile my styles.
What I want to achieve is to obtain a configurable theme that can change of color simply by changing some vars in my SASS files.
To do so :
I use "uncolored" SVGs as background for some elements of my website.
Then I apply a SVG filter for those elements to "colorize" them.
At this point, I'm pretty happy, because it is working... on almost all browsers : Brave, Chrome, Firefox, Opera, Edge
But it doesn't work on Safari and I'm not sure Why :
it seems that I cannot use the CSS filter with value url('data:image/svg+xml, <svg>...</svg>')
However it seems that I can use the filter property with url value if I'm targeting an element already in the DOM.
I've found an interesting article that talk about the same problem :
https://www.stefanjudis.com/today-i-learned/svgs-filters-can-be-inlined-in-css/#til%3A-svg-filters-can-be-inlined-in-css
I tested the included examples in Firefox 84 and and Chrome 87. It worked in Safari 14 but stopped for unknown reason.
The "included example" being inline SVG in the CSS
Do someone know if using inline CSS SVG is possible with Safari ?
Here is an example to illustrate the problem:
/* Basic style for parent */
.parent {
border: 3px dotted violet;
background: pink;
width: 400px;
height: 50px;
margin-bottom: 30px;
position: relative;
}
/* Basic style for children */
.children {
text-align: center;
color: #ffffff;
line-height: 40px;
}
.children::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
/* Using a pseudo element inside childrens that carry the background image */
.v0 .children::after, .v1 .children::after{
background-image: url('https://public.xspawn.fr/svg-bg-btn.svg');
background-repeat: no-repeat;
background-size: 100% 100%;
}
/*Creating a new stack context to be able to display text*/
.parent{position: relative; z-index:0;}
.children::after{ z-index:-1;}
/* Applying a filter to each children 'after' pseudo element to colorize its content (the SVG background or the background color) */
.children.recolor::after{
filter : url('data:image/svg+xml;utf8, <svg xmlns="http://www.w3.org/2000/svg"><filter id="recolor" color-interpolation-filters="sRGB"><feColorMatrix type="matrix" values="0 0 0 0 .13725 0 0 0 0 .23137 0 0 0 0 .32157 0 0 0 1 0"/></filter></svg>#recolor');
);
}
.children.recolor2::after{
filter : url('#dom-filter');
}
[v0] Element without Without SVG filter
<div class="parent v0">
<div class="children">No filter</div>
</div>
<strong style='color:orangered;'>[v1] KO : Filtered with "filter: url(data:image... )"</strong>
<div class="parent v1">
<div class="children recolor">filter: url(data:image..., < svg > .. .< svg \> )</div>
</div>
<strong style='color:green;'>[v2] OK : Filtered with filter DOM SVG and "filter: url('#elementid')"</strong>
<div class="parent v1">
<div class="children recolor2">filter: url('#dom-filter')</div>
</div>
<svg xmlns="http://www.w3.org/2000/svg"><filter id="dom-filter" color-interpolation-filters="sRGB"><feColorMatrix type="matrix" values="0 0 0 0 .13725 0 0 0 0 .23137 0 0 0 0 .32157 0 0 0 1 0"/></filter></svg>
Question: "Do someone know if using inline CSS SVG is possible with Safari ?"
Answer: "Yes it is possible".
If you use filters on svg elements in css/scss file like this
filter: url(#filter_id);
Transfer the styles from the css/scss file into the svg file separately into styles, and everything will work, like this:
<svg>
...
<g filter="url(#filter)">
..
</g>
<filter id="filter" x="5.54736" y="0" width={width-11.3} height="80.0002" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood floodOpacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="10" result="effect1_foregroundBlur_326_868"/>
</filter>
</svg>

Custom css file for readme.md in a Github repo

I am having trouble verifying the name of the .css file that will modifying the readme.md file at the root of Github repo.
I believe it is:
.github/github.css
but that doesn't seem to do anything to the Markdown. Does anyone know if this is incorrect?
You can add some HTML (actually XHTML) and CSS inside a <foreignObject> tag inside of an svg file and then embed that inside of an <img> tag in your GitHub README.
This is a simple animation in CSS that changes the color of the h1 text:
h1 {
color: red;
animation: myanimation 2s infinite;
}
#keyframes myanimation {
from {
color: red;
}
to {
color: yellow;
}
}
<h1>Hello world!</h1>
You can embed the style and HTML into a <foreignObject> tag inside of an svg like so:
example.svg
<svg fill="none" viewBox="0 0 400 400" width="400" height="400" xmlns="http://www.w3.org/2000/svg">
<foreignObject width="100%" height="100%">
<div xmlns="http://www.w3.org/1999/xhtml">
<style>
h1 {
color: red;
animation: mymove 2s infinite;
}
#keyframes mymove {
from {
color: red;
}
to {
color: yellow;
}
}
</style>
<h1>HELLO WORLD!</h1>
</div>
</foreignObject>
</svg>
Then, lastly you can embed the svg in your README, using an <img> tag and it should render your HTML with the applied CSS styles:
README.md
# My GitHub README
Welcome to my README!
<div align="center">
<img src="example.svg" width="400" height="400" alt="css-in-readme">
</div>
another example & source
GitHub does not allow for CSS to affect README.md files through CSS for security reasons (as if you could inject CSS into a ReadMe, you could easily launch a phishing attack). This includes both stylesheets referenced through <link rel> and inline styles used with <style>.
The readmes are in markdown syntax, so some styling can be done, such as adding colours through placeholder images, just like here on StackOverflow. For example, you can add red squares with the following:
- ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) `#f03c15`
You can also make use of things like diff, json, html, js and css to affect text colouring.
Wrap the images in a tag as shown below
<div>
<img src="/images/count.png" width="250" >
<img src="/images/home.png" width="250">
<img src="/images/profile.png" width="250">
</div>
Note: the images folder and the readme are in the root directory of the project

Resources