Can I optimise repeating SVG elements in an html page? - html

I'm using inline <svg> for my icons on an html page. I have a component which renders a table, often with hundreds of rows. In this case, every row of the table has the same inlined svg. Usually they are simple icons with just a few paths, but it got me wondering if there is some way to optimise this. In this scenario inlining is important, I don't want to have references to any external files. Is there some way for me to have the inlined svg just once, and refer to that same element somewhere else? Is this something I should even be worried about? Is the overhead of repeating inline svgs minimal?

If you have hundreds of table rows with one SVG, they all are (SVG) DOM elements you have to duplicate/repeat. There is no equivalent to SVGs use in HTML (you could built that yourself with a Custom Element)
If performance really really is an issue you need to test if creating SVGs Server Side (SSR) performs better (in your use case) than creating SVGs Client-Side... but you're probably talking micro-seconds..
The Custom Element IconMeister (one of my pet projects) creates all used SVG icons Client side
(you can cherry pick from 7000+ icons)
Or if all your SVGs are 100% duplicates just a one liner with native JS cloneNode could be enough.
Or create a very simple IconMeister yourself in a W3C Standard Custom Element
for clean semantic HTML:
(100% supported in all modern Browsers, supported by all Frameworks... except React)
<style>
svg-icon svg {
--size: 120px;
width: var(--size);
height: var(--size);
}
[bg=blue] svg{
width:1px;
}
[bg=yellow] svg{
width:5px;
}
</style>
<h3>Who is afraid of Red, Yellow and Blue</h3>
<svg-icon bg="red" ></svg-icon>
<svg-icon bg="yellow" ></svg-icon>
<svg-icon bg="blue" ></svg-icon>
<script>
customElements.define( "svg-icon", class extends HTMLElement {
connectedCallback() {
this.innerHTML =
`<svg xmlns='http://www.w3.org/2000/svg'>` +
`<rect width='100%' height='100%' fill='${this.getAttribute("bg")}'/>` +
`</svg>`;
}});
</script>

Related

SVG object change color from external CSS

I am playing around with SVGs (trying to replace icon fonts with SVG.) I got it to render the image/svg using object tag. However, I can't get it to change color from CSS. Assuming, I prefer coloring it from CSS, is there a way to do that while I use to embed SVG.
<object class="partnerLogo" type="image/svg+xml" data="assets/logos/sample.svg">
Your browser does not support SVG
</object>
CSS, I tried so far:
.partnerLogo {
width: 100%;
height: 100px;
color: red;
color-fill: red;
}
In sample.svg file, I added, <?xml-stylesheet type="text/css" href="../css/styles.css"?> just before
styles.css is being added to the page.
Thanks!
It isn't possible to directly modify the fill if you're using the SVG using the <object> method. The SVG is included as a document fragment inside the object tag, so your properties aren't passed as you can see in this image.
However, there are two ways you can modify the colors of an external SVG.
1) Use Javascript (recommended)
Using Javascript you can fetch the SVG contents via an XHR, and then inject it as inline SVG. As it's inline SVG technically, you can modify the fill color. There's a library I have written (svg-loader) that make it really easy to do this.
You just need to include the library and use data-src attributes to load SVGs.
Example:
Here, I have included a logo in three different formats, modifying the fill color.
<script type="text/javascript" src="https://unpkg.com/external-svg-loader#latest/svg-loader.min.js" async></script>
<div style="display:flex;">
<div style="background:black;">
<svg data-src="https://s2.svgbox.net/assets/logo-white.svg" fill="yellow"></svg>
</div>
<div style="background:purple;">
<svg data-src="https://s2.svgbox.net/assets/logo-white.svg" fill="white"></svg>
</div>
<div style="background:green;">
<svg data-src="https://s2.svgbox.net/assets/logo-white.svg" fill="red"></svg>
</div>
</div>
2) Use filter CSS property
You can use the filter CSS property to reach any color using bunch of operations (brightness, contrast, hue-rotate..). There an existing stack overflow discussion on this.
Example:
.red {
filter: invert(20%) sepia(97%) saturate(4013%) hue-rotate(353deg) brightness(93%) contrast(127%);
}
<img src="https://s2.svgbox.net/assets/logo-white.svg" class="red" />
The big drawback here is that you'd need to calculate this for every color (using this) and doesn't make it obvious how it works. Also, it won't work well with SVGs having multiple colors.
As far as I know, color in SVG-CSS should be stroke for borders and fill for backgrounds:
.partnerLogo {
width: 100%;
height: 100px;
stroke: red;
fill: red;
}
You can't use external CSS classes to style a SVG called within an < object > element, despite a lot of blog posts in the subject says you can interact with, buit this is misleading for this particular case. You must add the formattings inline, inside the actual SVG.
If you need to access and alter the actual objects and paths of an SVG from your main css file, you must embedd it inline, using the < svg > tag.
Here's a post that covers it all:
https://vecta.io/blog/best-way-to-embed-svg
I know this is an old question now - but this is for any future readers who want to colour their SVGs with pure CSS rather than have to use JS. I find this method quite convenient compared to other methods - and you can even colour your SVGs with a gradient etc.!
I simply make a div which will contain my SVG and give it a class.
HTML:
<div class="colourful-svg"></div>
Then the colour is done using masks and background colour in your CSS.
CSS:
.colourful-svg {
mask-image: url("path/to/your/svg-file.svg");
background: green;
// Make sure you define dimensions for your div otherwise it won't show up
height: 500px;
width: 500px;
mask-size: contain;
mask-position: center;
mask-repeat: no-repeat;
}
This will make your SVG fill the div you had made and therefore be the size you need it to. It then uses a mask to essentially only show your background colour through the SVG you have linked to using the url() function.
Masks now have pretty good support with prefixes (about 94% globally from caniuse.com at the time of writing), so I think this is quite a simple and easy way to implement colour SVGs - I hope someone finds this useful!

Is svg icon belong to style layer and view layer?

My team are shifting to svg icons.
We use to define the icons in css style classes, and are now considering whether SVG icons should be implemented as view component rendering svg markup (instead of html). This should allow for better reuse svg by changing dimensions, color etc. On the other hand this takes the icons out of the styling domain and any style change will cause code change (and not only css change).
Whats is the right way (if there any) to work with svg icons, control size and colors, and still save style layer and view layer?
SVG editors sometimes put style information inline, but style information for SVGs can also be provided inside <style> tags, as usual CSS.
If the SVG is in your DOM (the simplest way to do this, is to write the SVG directly into your HTML) the SVG can be formatted directly from your usual CSS file:
p {
color: #666;
}
.blueTriangle {
fill: lightblue;
stroke: #666;
stroke-width: 8;
}
<p> This is usual text in HTML p tags. </p>
<p> The next thing is embedded SVG, styled by usual CSS: </p>
<svg width="250" height="120">
<path class="blueTriangle" d="M150 20 L130 90 L170 90 Z" />
</svg>
With transform also scaling and rotations can be done, showing different components can be accomplished via display, visibility or opacity with different implications.
Changing paths themselves would be considered as changing the content of the file and can be done via JavaScript. Therefore the separation of style an content can still be preserved.

GWT + CSS Style Overloading

I'm looking at making some custom GWT widgets styled in a uniform fashion without requiring the designer to go into each widget's UI file every time they want something to appear differently. I would provide a bunch of base styles for elements in the widget and the designer comes along later and sets, in UIBinder, HTML, CSS, anything really, the style using the same selector.
For example:
I have a composite widget which I have setup to use a CSSResource. This CSS resource has a style named .myHeaderStyle which is applied to an element on the composite.
This composite is used in another GWT Widget and needs to appear slightly differently when used in the enclosing widget.
My hope here is that I can specify another style in the UIBinder definition of that UI also named .myHeaderStyle and have this style override the style specified in the composite widget's CSSResource.
However, in my attempts to make this happen even with !important included on the style properties that are to override the initial style, I'm only getting the original .myHeaderStyle set on the composite widget.
I'm trying to specifically avoid adding/changing the style in the composite every time we compile, I want it to inherit from the enclosing page effectively overriding the composite widget's original styling.
Is what I'm trying to do possible in some form with GWT+CSS?
After building complex GWT apps for 6 years, I am a big proponent of using a single CSS file for the entire app, and never using CSS resources or UIBinder definitions. Then you can set ".myWidget" style in your widget, and your designer can do:
.myHeaderStyle {
font-size: 1.4rem;
}
.myWidget .myHeaderStyle {
font-size: 1.6rem;
}
In my opinion, this is the easiest way to maintain consistency throughout the app - all styles are in one place, using inheritance, rem, and other best practices. It's much easier for designers that CSS resources scattered throughout the app.
By the way, this is also the easiest approach to implement themes (or skins), or change CSS based on the screen size without touching the code.
EDIT:
This is an example from one of my apps:
<g:HTMLPanel>
<g:Label ui:field="logo" styleName="logo">My App</g:Label>
<div class="menu" >
<div class="tab" >
<g:Label ui:field="tab1" ><ui:text from="{constants.tab1}" /></g:Label>
<g:Label ui:field="tab2" ><ui:text from="{constants.tab2}" /></g:Label>
<g:Label ui:field="tab3" ><ui:text from="{constants.tab3}" /></g:Label>
</div>
</div>
</g:HTMLPanel>
Note that I use 'class' for div element, but styleName for a widget. I don't set style on my tab labels, because I use CSS to style all of them at once:
.tab>div {
float: right;
margin: 0 0 0 6px;
padding: 2px 6px;
width: 120px;
}

GIF as css background image

I have something like this in css
.ajax-loader {
background-image: url(../images/icon/loader.gif);
}
and used on HTML elements like this
<div class='ajax-loader'></div>
my question is:
If the ajax-loader style is used multiple times in a single page, will the image loader.gif be loaded multiple times or only once?
This image will be loaded once.This is very simple like object-oriented programing.Define a class once and use everywhere.The same thing is followed in css.
Once the image is loaded you can use it everywhere and also this is a good programming approach

Injecting HTML via CSS

I need to basically set the content of something with HTML from CSS. I'm currently doing the following:
.myclass {
content "<img src=\"hello.png\"/>";
}
However, instead of the image, I see the literal text:
<img src="hello.png"/>
How can I inject arbitrary HTML using CSS?
HTML stores the data, and is the Model
CSS stores the styles, and is the View
JS stores the interactions, and is the Controller
If you're trying to add data to the page, it should be done via HTML. If you're simply trying to add an image as a style, use the background-image property. You don't need to inject an <img> element in the page to do that.
Don't ever do this, ever
As far as being able to inject HTML into the page via CSS, it's not directly possible, however it's possible to add JavaScript into the page using CSS, which can then add HTML to the page.
I can't emphasize enough how wrong that approach would be.
Unless there is some strange hack that I am not aware of, this cannot be done with pure CSS.
The content property is only able to insert text; if you try to put in HTML, it will be escaped.
That said, you can do something like this with pure CSS:
This is the CSS that can perform that effect:
.myClass:before {
display: inline-block;
width: 32px;
height: 32px;
content: "";
background-image: url("img.gif");
}
You can see this in action on this jsFiddle page.
In this particular case, you can use a pseudo-class (eg ::before), background-image, display:block and a fixed width and height to show the image.
Also, make sure that the colon : is added between content and its value.
A relatively new concept at the horizon is the element() value for backgrounds. This will display HTML as if it were an image: See also -moz-element.
This can be done. For example with Firefox
css
#hlinks
{
-moz-binding: url(stackexchange.xml#hlinks);
}
stackexchange.xml
<bindings xmlns="http://www.mozilla.org/xbl"
xmlns:html="http://www.w3.org/1999/xhtml">
<binding id="hlinks">
<content>
<children/>
<html:a href="/privileges">privileges</html:a>
<html:span class="lsep"> | </html:span>
<html:a href="/users/logout">log out</html:a>
</content>
</binding>
</bindings>
ref 1
ref 2
You can't. It's not what it's for. The CSS is for the presentation layer while the HTML is the data layer.
Actually, you can, but just for characters, not HTML. You can use the content property. With some CSS selectors like :before, you can do nice stuff like adding your own character as a bullet for your list. But not much more.