Como estilizar SVG com CSS externo?

102

Tenho vários gráficos SVG que gostaria de modificar as cores por meio de minhas folhas de estilo externas - não diretamente em cada arquivo SVG. Não estou colocando os gráficos em linha, mas armazenando-os na minha pasta de imagens e apontando para eles.

Eu os implementei dessa forma para permitir que as dicas de ferramentas funcionassem e também envolvi cada um em uma <a>tag para permitir um link.

<a href='http://youtube.com/...' target='_blank'><img class='socIcon' src='images/socYouTube.svg' title='View my videos on YouTube' alt='YouTube' /></a>

E aqui está o código do gráfico SVG:

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="stylesheets/main.css" type="text/css"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 56.69 56.69">
<g>
    <path d="M28.44......./>
</g>
</svg>

Coloquei o seguinte em meu arquivo CSS externo (main.css):

.socIcon g {fill:red;}

No entanto, não tem efeito no gráfico. Também tentei o caminho .socIcon g {} e o caminho .socIcon {}.

Algo não está certo, talvez minha implementação não permita modificações externas de CSS ou eu perdi uma etapa. Eu realmente aprecio sua ajuda! Eu só preciso modificar as cores do gráfico SVG por meio de minha folha de estilo externa, mas não posso perder a dica de ferramenta e a capacidade de link. (No entanto, posso viver sem dicas.) Obrigado!

Jordan H
fonte
Consulte stackoverflow.com/questions/12604095/…
Erik Dahlström
Experimente svg { fill:red; }ou dê ao seu caminho um nome de classe. Por exemplo, <path class="socIcon" d="M28.44 ..... />isso deve funcionar.
Dwza

Respostas:

92

Seu arquivo main.css só terá efeito no conteúdo do SVG se o arquivo SVG for incluído in-line no HTML:

https://developer.mozilla.org/en/docs/SVG_In_HTML_Introduction

<html>
  <body>
  <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 56.69 56.69">
    <g>
      <path d="M28.44......./>
    </g>
  </svg>
</html>

Se você deseja manter seu SVG em arquivos, o CSS precisa ser definido dentro do arquivo SVG.

Você pode fazer isso com uma tag de estilo:

http://www.w3.org/TR/SVG/styling.html#StyleElementExample

<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
     width="50px" height="50px" viewBox="0 0 50 50">
  <defs>
    <style type="text/css"><![CDATA[
      .socIcon g {
        fill:red;
      }
    ]]></style>
  </defs>
  <g>
    <path d="M28.44......./>
  </g>
</svg>

Você pode usar uma ferramenta no lado do servidor para atualizar a tag de estilo dependendo do estilo ativo. Em rubi, você pode conseguir isso com Nokogiri. SVG é apenas XML. Portanto, provavelmente há muitas bibliotecas XML disponíveis que provavelmente podem fazer isso.

Se você não puder fazer isso, terá que apenas usá-los como se fossem PNGs; criando um conjunto para cada estilo e salvando seus estilos embutidos.

RGB
fonte
17
Isso significa que não há nenhum método para se beneficiar do armazenamento em cache do SVG e da aplicação de estilos variados? Inline não parece armazenar bem em cache, enquanto outros métodos exigiriam a criação de várias versões da imagem, eliminando qualquer benefício de armazená-las em cache.
msg45f
Outra forma é codificar os SVGs como uris de dados de imagem de fundo, com cores diferentes em cada versão e usar o gzip para reduzir o tamanho do arquivo devido à duplicação.
Ruskin
1
No meu caso, eu queria substituir os estilos de elemento do SVG. Meu CSS não funcionou, porque os estilos de elemento tinham uma prioridade mais alta. A solução mais simples foi adicionar um! Important ao estilo CSS do SVG. Então tudo ficou bem. Se você deseja evitar! Important, você precisa mover os estilos de elemento para o CSS.
David Gausmann
pena que você não pode carregar uma folha de estilo dentro de um svg de um URL
clayRay
1
@clayRay, você poderá fazer isso assim que o SVG2 estiver completo, rascunho w3.org/TR/SVG2/styling.html#LinkElement
RGB
51

Você pode fazer o que quiser, com uma advertência (importante): os caminhos dentro do seu símbolo não podem ser estilizados de forma independente por meio de CSS externo - você só pode definir as propriedades de todo o símbolo com este método. Portanto, se você tiver dois caminhos em seu símbolo e quiser que eles tenham cores de preenchimento diferentes, isso não funcionará, mas se quiser que todos os seus caminhos sejam iguais, isso deve funcionar.

Em seu arquivo html, você deseja algo assim:

<style>
  .fill-red { fill: red; }
  .fill-blue { fill: blue; }
</style>

<a href="//www.example.com/">
  <svg class="fill-red">
    <use xlink:href="images/icons.svg#example"></use>
  </svg>
</a>

E no arquivo SVG externo, você deseja algo assim:

<svg xmlns="http://www.w3.org/2000/svg">
   <symbol id="example" viewBox="0 0 256 256">
    <path d="M120.... />
  </symbol>
</svg>

Troque a classe na svgtag (em seu html) de fill-redpara fill-bluee ta-da ... você tem azul em vez de vermelho.

Você pode contornar parcialmente a limitação de ser capaz de direcionar os caminhos separadamente com CSS externo misturando e combinando o CSS externo com algum CSS in-line em caminhos específicos, já que o CSS in-line terá precedência. Essa abordagem funcionaria se você estiver fazendo algo como um ícone branco contra um fundo colorido, onde deseja alterar a cor do fundo por meio do CSS externo, mas o ícone em si é sempre branco (ou vice-versa). Então, com o mesmo HTML de antes e algo como este código SVG, você obterá um fundo vermelho e um caminho de primeiro plano branco:

<svg xmlns="http://www.w3.org/2000/svg">
  <symbol id="example" viewBox="0 0 256 256">
    <path class="background" d="M120..." />
    <path class="icon" style="fill: white;" d="M20..." />
  </symbol>
</svg>
Adam Korman
fonte
boa resposta ... acho que a advertência deveria ser: Suporte ao navegador, no entanto! boa referência (mais detalhes do que caniuse): css-tricks.com/svg-fragment-identifiers-work
ptim
Esta é uma solução! Na verdade, não há necessidade de envolver todo o conteúdo svg em um symbolelemento, ou seja, você pode apenas fornecer um idao elemento svg, então: `<svg id = example" xmlns = " w3.org/2000/svg " viewBox = "0 0 256 256 "> <path class =" background "d =" M120 ... "/> <path class =" icon "style =" fill: white; "d =" M20 ... "/> </ svg > `
SimoneMSR
12

Você pode incluir em seus arquivos SVG um link para um arquivo css externo usando:

<link xmlns="http://www.w3.org/1999/xhtml" rel="stylesheet" href="mystyles.css" type="text/css"/>

Você precisa colocar isso depois de abrir a tag:

<svg>
  <link xmlns="http://www.w3.org/1999/xhtml" rel="stylesheet" href="mystyles.css" type="text/css"/>
  <g>
    <path d=.../>
  </g>
</svg>

Não é a solução perfeita, porque você tem que modificar os arquivos svg, mas você os modifica uma vez e então todas as mudanças de estilo podem ser feitas em um arquivo css para todos os arquivos svg.

Jacek Mamot
fonte
1
Uau, isso funciona, mas apenas 1 voto positivo ... essa solução é boa para todas as situações? É tão simples, por que não foi essa a resposta escolhida?
Bruno Vincent,
O objetivo é centralizar suas definições de estilo. Digamos que você tenha 10 SVGs que deseja estilizar. Agora você precisa copiar uma referência ao CSS em todos os SVGs que precisam ser afetados. E se o nome / localização do arquivo de seu CSS mudar, você precisará atualizá-lo em 10 SVGs. Uma classe CSS parece muito mais simbólica do que uma referência a um arquivo CSS físico.
Frans
Observe que svg carregado por meio de uma tag <img> não carregará conteúdo externo (por exemplo, folhas de estilo).
Moose Morals
7

É possível definir o estilo de um SVG criando dinamicamente um elemento de estilo em JavaScript e anexando-o ao elemento SVG. Hacky, mas funciona.

<object id="dynamic-svg" type="image/svg+xml" data="your-svg.svg">
    Your browser does not support SVG
</object>
<script>
    var svgHolder = document.querySelector('object#dynamic-svg');
    svgHolder.onload = function () {
        var svgDocument = svgHolder.contentDocument;
        var style = svgDocument.createElementNS("http://www.w3.org/2000/svg", "style");

        // Now (ab)use the @import directive to load make the browser load our css
        style.textContent = '@import url("/css/your-dynamic-css.css");';

        var svgElem = svgDocument.querySelector('svg');
        svgElem.insertBefore(style, svgElem.firstChild);
    };
</script>

Você poderia gerar o JavaScript dinamicamente em PHP se quiser - o fato de isso ser possível em JavaScript abre uma infinidade de possibilidades.

Pete
fonte
Ei, na verdade, gosto da solução e está funcionando, mas preciso adotá-la na minha situação se você estiver disposto a ajudar, é claro, tenho dentro de <defs> uma tag de estilo que posso excluí-los manualmente e executar este código para criar um estilo, há uma maneira de eu poder excluir os defs e recriar o elemento como você fez ou apenas atualizá-lo, e também o url tem um erro de url não está definido, você pode ajudar, obrigado
Kamel Mili
6

Uma abordagem que você pode adotar é apenas usar filtros CSS para alterar a aparência dos gráficos SVG no navegador.

Por exemplo, se você tiver um gráfico SVG que usa uma cor de preenchimento de vermelho no código SVG, pode torná-lo roxo com uma configuração de rotação de matiz de 180 graus:

#theIdOfTheImgTagWithTheSVGInIt {
    filter: hue-rotate(180deg);
    -webkit-filter: hue-rotate(180deg);
    -moz-filter: hue-rotate(180deg);
    -o-filter: hue-rotate(180deg);
    -ms-filter: hue-rotate(180deg);
}

Experimente outras configurações de rotação de matiz para encontrar as cores desejadas.

Para ser claro, o CSS acima vai no CSS que é aplicado ao seu documento HTML. Você está estilizando a tag img no código HTML, não estilizando o código do SVG.

E observe que isso não funcionará com gráficos com preenchimento em preto, branco ou cinza. Você tem que ter uma cor real lá para girar o matiz dessa cor.

Simon White
fonte
4

Uma solução muito rápida para ter um estilo dinâmico com uma folha de estilo CSS externa, caso você esteja usando o <object> tag para incorporar seu svg.

Este exemplo adicionará uma classe à raiz <svg> tag ao clicar em um elemento pai.

file.svg:

<?xml-stylesheet type="text/css" href="../svg.css"?>
 <svg xmlns="http://www.w3.org/2000/svg" viewBox="">
  <g>
   <path/>
  </g>
 </svg>

html:

<a class="parent">
  <object data="file.svg"></object>
</a>

Jquery:

$(function() {
  $(document).on('click', '.parent', function(){
    $(this).find('object').contents().find('svg').attr("class","selected");
  }
});

no elemento pai do clique:

 <svg xmlns="http://www.w3.org/2000/svg" viewBox="" class="selected">

então você pode gerenciar seu css

svg.css:

path {
 fill:none;
 stroke:#000;
 stroke-miterlimit:1.41;
 stroke-width:0.7px;
}

.selected path {
 fill:none;
 stroke:rgb(64, 136, 209);
 stroke-miterlimit:1.41;
 stroke-width:0.7px;
}
Vhanahrni
fonte
parece não funcionar, você poderia adicionar um exemplo funcional?
Jeanluca Scaljeri
4

Deve ser possível fazer in-line primeiro as imagens SVG externas. O código abaixo vem da substituição de todas as imagens SVG por SVG embutido de Jess Frazelle.

$('img.svg').each(function(){
  var $img = $(this);
  var imgID = $img.attr('id');
  var imgClass = $img.attr('class');
  var imgURL = $img.attr('src');
  $.get(imgURL, function(data) {
    // Get the SVG tag, ignore the rest
    var $svg = $(data).find('svg');
    // Add replaced image's ID to the new SVG
    if (typeof imgID !== 'undefined') {
      $svg = $svg.attr('id', imgID);
    }
    // Add replaced image's classes to the new SVG
    if (typeof imgClass !== 'undefined') {
      $svg = $svg.attr('class', imgClass+' replaced-svg');
    }
    // Remove any invalid XML tags as per http:validator.w3.org
    $svg = $svg.removeAttr('xmlns:a');
    // Replace image with new SVG
    $img.replaceWith($svg);
  });
});
Leo
fonte
3
É importante observar que isso funcionará apenas se você tiver a imagem hospedada no mesmo domínio do html, ou se tiver uma política de domínio cruzado especialmente configurada no servidor de imagem. $ .get usará ajax e falhará ao carregar a imagem do servidor externo se não houver cabeçalho de permissão de acesso válido
user151496
isso é lendário
Tino Costa 'El Nino'
2

Quando usado em uma <image>tag, o SVG deve estar contido em um único arquivo por motivos de privacidade. Este bug do bugzilla tem mais detalhes sobre o motivo exato disso. Infelizmente, você não pode usar uma tag diferente, como um <iframe>porque isso não funcionará como um link, então você terá que incorporar o CSS em um<style> tag dentro do próprio arquivo.

Uma outra maneira de fazer isso seria ter os dados SVG dentro do arquivo html principal, ou seja,

<a href='http://youtube.com/...' target='_blank'>
  <svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 56.69 56.69">
    <g>
        <path d="M28.44......./>
    </g>
  </svg>
</a>

Você pode estilizar isso com um arquivo CSS externo usando a <link>tag HTML .

Robert Longson
fonte
2
Não consigo colocar os estilos nos arquivos. Na verdade, vou mudar as cores dessas imagens com base no esquema de cores que o usuário escolheu para o meu site. Minha implementação atual é adicionar uma folha de estilo à página principal que substitui os estilos padrão. Eu queria colocar as alterações de cor nesse arquivo para afetar os gráficos SVG incorporados. Mas isso não funcionaria porque eu tenho que criar um link para a folha de estilo de dentro do arquivo SVG (a menos que haja uma maneira de adicionar esse link a todos os arquivos SVG usando JavaScript também). Certamente há uma maneira de permitir arquivos SVG externos, CSS externos, links e dicas de ferramentas.
Jordan H
1
Não é possível fazer o que você deseja por causa do modelo de segurança do navegador. Você não pode usar javascript para manipular SVG quando usado como uma imagem. Pense no SVG quando usado como uma imagem como um arquivo png ou gif animado, tudo em um arquivo e sem acesso de script.
Robert Longson
1

"Na verdade, vou mudar as cores dessas imagens com base no esquema de cores que o usuário escolheu para o meu site." - Jordan 10 horas atrás

Eu sugiro que você use PHP para isso. Realmente não há maneira melhor de fazer isso sem fontes de ícone e, se você resistir a usá-las, pode tentar o seguinte:

<?php

    header('Content-Type: image/svg+xml');
    echo '<?xml version="1.0" encoding="utf-8"?>';
    $color = $_GET['color'];

?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 56.69 56.69">
    <g>
        <path fill="<?php echo $color; ?>" d="M28.44..."/>
    </g>
</svg>

E mais tarde você pode usar este arquivo filename.php?color=#ffffffpara obter o arquivo svg na cor desejada.

SeinopSys
fonte
6
Observe que este código não verifica a entrada do usuário - qualquer coisa pode ser fornecida como cor e seu SVG pode ser renderizado de algumas maneiras muito interessantes ... Não tenho certeza se isso afeta você, mas você deve criar o hábito de SEMPRE validar a entrada do usuário. Algo como isso ajudaria: if (!preg_match('/^[#][0-9a-f]{6}$/i', $_GET['color'])) die('Oops!');(coloque em algum lugar no bloco de início do PHP).
johndodo
1

O que funciona para mim: tag de estilo com regra @import

<defs>
    <style type="text/css">
        @import url("svg-common.css");
    </style>
</defs>
Fordi
fonte
1

Eu sei que é um post antigo, mas só para esclarecer esse problema ... você acabou de usar suas aulas no lugar errado: D

Em primeiro lugar, você poderia usar

svg { fill: red; }

em seu main.csspara deixá-lo vermelho. Isso tem efeito. Provavelmente, você também pode usar seletores de nó para obter caminhos específicos.

A segunda coisa é que você declarou a classe à img-Tag.

<img class='socIcon'....

Você realmente deve declará-lo dentro do seu SVG. se você tiver diferentes caminhos, poderá definir mais, é claro.

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="stylesheets/main.css" type="text/css"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 56.69 56.69">
<g>
    <path class="myClassForMyPath" d="M28.44......./>
</g>
</svg>

agora você pode mudar a cor do seu main.cssgosto

.myClassForMyPath {
    fill: yellow;
}
Dwza
fonte
Tentei fazer isso não funcionou, como muitas das outras respostas já dizem. Você não pode aplicar estilo a classes dentro do SVG.
Frans
@Frans você está incluindo um svg como um arquivo ou você tem sua fonte svg como no exemplo acima? Porque tenho em mente que isso depende de como você usa o svg. Incluindo por img não vai funcionar.
Dwza
Exatamente, ele só funciona se você embutir o SVG em seu HTML. Mas não é isso que seu exemplo faz. Ele usa um SVG externo (ou seja, não embutido). Parece não haver maneira de estilizar um SVG externo com CSS em seu HTML.
Frans
certeza de que experimentou meu exemplo correto? Quer dizer, incluiu um arquivo css externo? <?xml-stylesheet href="stylesheets/main.css" type="text/css"?>
Dwza
OK, agora eu vejo, você tem um <?xml-stylesheet ... ?> declaração dentro do seu SVG. Eu acho que funcionaria. É semelhante a outras respostas que recomendam um <link rel="stylesheet" ... >dentro do SVG. Ele também tem os mesmos problemas (você precisa atualizar cada SVG para apontar para a folha de estilo, e qualquer mudança no nome ou localização da folha de estilo significa ter que mudar todos os SVGs novamente).
Frans
1
  1. Para estilos externos

<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 56.69 56.69">

  <style>
	@import url(main.css);
  </style>

  <g>
    <path d="M28.44......./>
  </g>
</svg>

  1. Para estilos internos

<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 56.69 56.69">

  <style>
	    .socIcon g {fill:red;}
  </style>

  <g>
    <path d="M28.44......./>
  </g>
</svg>

Nota: Estilos externos não funcionarão se você incluir SVG dentro <img> tag . Vai funcionar perfeitamente dentro da <div>tag

Yuvraj Patil
fonte
0

@leo aqui está a versão angularJS, obrigado novamente

G.directive ( 'imgInlineSvg', function () {

return {
    restrict : 'C',
    scope : true,
    link : function ( scope, elem, attrs ) {

        if ( attrs.src ) {

            $ ( attrs ).each ( function () {
                var imgID    = attrs.class;
                var imgClass = attrs.class;
                var imgURL   = attrs.src;

                $.get ( imgURL, function ( data ) {

                    var $svg = $ ( data ).find ( 'svg' );
                    if ( typeof imgID !== 'undefined' ) {
                        $svg = $svg.attr ( 'id', imgID );
                    }

                    if ( typeof imgClass !== 'undefined' ) {
                        $svg = $svg.attr ( 'class', imgClass + ' replaced-svg' );
                    }

                    $svg = $svg.removeAttr ( 'xmlns:a' );

                    elem.replaceWith ( $svg );

                } );

            } );
        }

    }

}

} );
Tino Costa 'El Niño'
fonte
-1

Este método funcionará se o svg for visualizado em um navegador da web, mas assim que este código for carregado para o servidor e a classe para o ícone svg for codificada como se fosse uma imagem de fundo a cor é perdida e volta para a cor padrão . Parece que a cor não pode ser alterada na folha de estilo externa, embora ambas as classes svg para a cor e a classe da camada superior para a exibição e posição do svg estejam mapeadas para o mesmo diretório.

HTMLGUY1999
fonte