¿Cómo diseñar SVG con CSS externo?


102

Tengo varios gráficos SVG cuyos colores me gustaría modificar a través de mis hojas de estilo externas, no directamente dentro de cada archivo SVG. No estoy colocando los gráficos en línea, sino almacenándolos en mi carpeta de imágenes y señalándolos.

Los he implementado de esta manera para permitir que la información sobre herramientas funcione, y también los he incluido en una <a>etiqueta para permitir un enlace.

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

Y aquí está el código del 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>

Puse lo siguiente en mi archivo CSS externo (main.css):

.socIcon g {fill:red;}

Sin embargo, no tiene ningún efecto sobre el gráfico. También probé .socIcon g path {} y .socIcon path {}.

Algo no está bien, tal vez mi implementación no permite modificaciones externas de CSS o me perdí un paso. ¡Realmente agradecería su ayuda! Solo necesito la capacidad de modificar los colores del gráfico SVG a través de mi hoja de estilo externa, pero no puedo perder la información sobre herramientas y la capacidad de enlace. (Aunque puede que pueda vivir sin la información sobre herramientas). ¡Gracias!



Prueba svg { fill:red; }o dale a tu ruta un nombre de clase. Por ejemplo, <path class="socIcon" d="M28.44 ..... />esto debería funcionar.
Dwza

Respuestas:


92

Su archivo main.css solo tendría efecto en el contenido del SVG si el archivo SVG está incluido en línea en el 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>

Si desea mantener su SVG en archivos, el CSS debe definirse dentro del archivo SVG.

Puedes hacerlo con una etiqueta 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>

Puede usar una herramienta en el lado del servidor para actualizar la etiqueta de estilo según el estilo activo. En rubí podrías lograr esto con Nokogiri. SVG es solo XML. Así que probablemente haya muchas bibliotecas XML disponibles que probablemente puedan lograr esto.

Si no puede hacer eso, tendrá que usarlos como si fueran PNG; creando un conjunto para cada estilo y guardando sus estilos en línea.


17
¿Significa esto que no existe un método para beneficiarse del almacenamiento en caché del SVG y la aplicación de estilos variados? Inline no parece almacenar en caché bien, mientras que otros métodos requerirían crear muchas versiones de la imagen, eliminando cualquier beneficio de almacenarlas en caché.
msg45f

Otra forma es codificar los SVG como uris de datos de imagen de fondo, con diferentes colores en cada versión y confiar en gzip para reducir el tamaño del archivo debido a la duplicación.
Ruskin

1
En mi caso, quería anular los estilos de elementos del SVG. Mi CSS no funcionó porque los estilos de los elementos tenían una prioridad más alta. La solución más simple fue agregar un! Important al estilo CSS para el SVG. Entonces todo estuvo bien. Si desea evitar! Important, debe mover los estilos de los elementos al CSS.
David Gausmann

lástima que no pueda cargar una hoja de estilo dentro de un svg desde una URL
ClayRay

1
@clayRay, podrá hacerlo de esa manera una vez que SVG2 esté completo como borrador w3.org/TR/SVG2/styling.html#LinkElement
RGB

51

Puede hacer lo que quiera, con una advertencia (importante): las rutas dentro de su símbolo no se pueden diseñar de forma independiente a través de CSS externo; solo puede establecer las propiedades para todo el símbolo con este método. Entonces, si tiene dos rutas en su símbolo y desea que tengan colores de relleno diferentes, esto no funcionará, pero si desea que todas sus rutas sean iguales, esto debería funcionar.

En su archivo html, quiere algo como esto:

<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>

Y en el archivo SVG externo quieres algo como esto:

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

Cambiar la clase en el svg etiqueta (en tu html) de fill-redto fill-bluey ta-da ... tienes azul en lugar de rojo.

Puede sortear parcialmente la limitación de poder apuntar a las rutas por separado con CSS externo mezclando y haciendo coincidir el CSS externo con algo de CSS en línea en rutas específicas, ya que el CSS en línea tendrá prioridad. Este enfoque funcionaría si está haciendo algo como un icono blanco sobre un fondo de color, donde desea cambiar el color del fondo a través del CSS externo, pero el icono en sí es siempre blanco (o viceversa). Entonces, con el mismo HTML que antes y algo como este código svg, obtendrá un fondo rojo y una ruta de primer plano blanca:

<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>

buena respuesta ... creo que la advertencia debería ser: ¡Sin embargo, soporte para navegadores! buena referencia (más detalles que caniuse): css-tricks.com/svg-fragment-identifiers-work
ptim

¡Ésta es una solución! En realidad, no hay necesidad de envolver todo el contenido de svg en un symbolelemento, es decir, puede simplemente dar un idal elemento svg, entonces: `<svg id = example" xmlns = " w3.org/2000/svg " viewBox = "0 0256256 "> <path class =" background "d =" M120 ... "/> <path class =" icon "style =" fill: white; "d =" M20 ... "/> </ svg > `
SimoneMSR

11

Puede incluir en sus archivos SVG un enlace a un archivo css externo usando:

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

Debe poner esto después de abrir la etiqueta:

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

No es la solución perfecta, porque tiene que modificar los archivos svg, pero los modifica una vez y todos los cambios de estilo se pueden realizar en un archivo css para todos los archivos svg.


Vaya, esto funciona, pero solo 1 voto a favor ... ¿esta solución es buena para todas las situaciones? Es tan simple, ¿por qué no es esta la respuesta elegida?
Bruno Vincent

El punto es centralizar sus definiciones de estilo. Digamos que tienes 10 SVG que quieres diseñar. Ahora debe copiar una referencia al CSS en todos los SVG que deben verse afectados. Y si el nombre de archivo / ubicación de su CSS cambia, debe actualizarlo en 10 SVG. Una clase CSS se siente mucho más simbólica que una referencia a un archivo CSS físico.
Frans

Tenga en cuenta que svg cargado a través de una etiqueta <img> no cargará contenido externo (por ejemplo, hojas de estilo).
Moose Morals

7

Es posible diseñar un SVG creando dinámicamente un elemento de estilo en JavaScript y agregándolo al elemento SVG. Hacky, pero 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>

Puede generar el JavaScript dinámicamente en PHP si lo desea; el hecho de que esto sea posible en JavaScript abre una miríada de posibilidades.


Oye, de hecho, me gusta tu solución y está funcionando, pero necesito adoptarla en mi situación si estás dispuesto a ayudar, por supuesto, tengo dentro de <defs> una etiqueta de estilo que puedo eliminar manualmente y ejecutar este código para crear un estilo, ¿hay alguna manera de eliminar las definiciones y luego volver a crear el elemento como lo hizo usted o simplemente actualizarlo, y también la url tiene un error? La url no está definida. ¿Pueden ayudar, gracias?
Kamel Mili

6

Un enfoque que puede tomar es usar filtros CSS para cambiar la apariencia de los gráficos SVG en el navegador.

Por ejemplo, si tiene un gráfico SVG que usa un color de relleno rojo dentro del código SVG, puede convertirlo en violeta con una configuración de rotación de tono de 180 grados:

#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 con otros ajustes de rotación de tonos para encontrar los colores que desee.

Para ser claros, el CSS anterior va en el CSS que se aplica a su documento HTML. Está aplicando estilo a la etiqueta img en el código HTML, no aplicando estilo al código del SVG.

Y tenga en cuenta que esto no funcionará con gráficos que tengan un relleno de negro, blanco o gris. Tienes que tener un color real allí para rotar el tono de ese color.


4

Una solución muy rápida para tener un estilo dinámico con una hoja de estilo CSS externa, en caso de que esté usando la <object>etiqueta para incrustar su svg.

Este ejemplo agregará una clase a la <svg>etiqueta raíz al hacer clic en un elemento principal.

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");
  }
});

al hacer clic en el elemento principal:

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

entonces puedes administrar tu 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;
}

no parece funcionar, ¿podría agregar un ejemplo funcional?
Jeanluca Scaljeri

4

Debería ser posible hacerlo insertando primero las imágenes svg externas. El siguiente código proviene de reemplazar todas las imágenes SVG con SVG en línea 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);
  });
});

3
Es importante tener en cuenta que esto solo funcionará si tiene la imagen alojada en el mismo dominio que el html o si tiene una política de dominio cruzado especialmente configurada en el servidor de imágenes. $ .get usará ajax y no podrá cargar la imagen desde un servidor externo si no hay un encabezado de
permiso de

esto es legendario
Tino Costa 'El Niño'

2

Cuando se usa en una <image>etiqueta, SVG debe estar contenido en un solo archivo por razones de privacidad. Este error de bugzilla tiene más detalles sobre exactamente por qué es así. Lamentablemente, no puede usar una etiqueta diferente, como un, <iframe>porque no funcionará como un enlace, por lo que tendrá que incrustar el CSS en un<style> etiqueta dentro del archivo.

Otra forma de hacer esto sería tener los datos SVG dentro del archivo html principal, es decir

<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>

Puede diseñar eso con un archivo CSS externo usando la <link>etiqueta HTML .


2
No puedo poner los estilos dentro de los archivos. De hecho, voy a cambiar los colores de estas imágenes en función de la combinación de colores que el usuario haya elegido para mi sitio. Mi implementación actual es agregar una hoja de estilo a la página principal que sobrescribe los estilos predeterminados. Quería poner los cambios de color en ese archivo para afectar los gráficos SVG incrustados. Pero eso no funcionaría porque tengo que vincularme a la hoja de estilo desde el archivo SVG (a menos que haya una manera de agregar ese vínculo a todos los archivos SVG usando JavaScript también). Seguramente hay una manera de permitir archivos SVG externos, CSS externos, enlaces e información sobre herramientas.
Jordan H

1
No es posible hacer lo que quiere hacer debido al modelo de seguridad del navegador. No puede usar javascript para manipular SVG cuando se usa como una imagen. Piense en SVG cuando se usa como una imagen como un archivo png o gif animado, todo en un archivo y sin acceso a secuencias de comandos.
Robert Longson

1

"De hecho, voy a cambiar los colores de estas imágenes en función de la combinación de colores que el usuario haya elegido para mi sitio". - Jordan hace 10 horas

Le sugiero que use PHP para esto. Realmente no hay mejor manera de hacer esto sin fuentes de iconos, y si se resiste a usarlas, puede intentar esto:

<?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>

Y luego podría usar este archivo filename.php?color=#ffffffpara obtener el archivo svg en el color deseado.


6
Tenga en cuenta que este código no verifica la entrada del usuario; cualquier cosa podría proporcionarse como color y su SVG podría mostrarse de algunas formas muy interesantes ... No estoy seguro si le afecta, pero debe acostumbrarse a SIEMPRE validar la entrada del usuario. Algo como esto ayudaría: if (!preg_match('/^[#][0-9a-f]{6}$/i', $_GET['color'])) die('Oops!');(colóquelo en algún lugar del bloque PHP de inicio).
johndodo

1

Lo que me funciona: etiqueta de estilo con regla @import

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

1

Sé que es una publicación antigua, pero solo para solucionar este problema ... estás usando tus clases en el lugar equivocado: D

Primero que nada podrías usar

svg { fill: red; }

en tus main.css para ponerlo rojo. Esto tiene efecto. También podría usar selectores de nodos para obtener parches específicos.

En segundo lugar, declaró la clase a la imgetiqueta -Tag.

<img class='socIcon'....

En realidad, debería declararlo dentro de su SVG. si tiene diferentes patrones, podría definir más, por supuesto.

<?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>

ahora puedes cambiar el color a tu main.cssgusto

.myClassForMyPath {
    fill: yellow;
}

Intenté esto, no funciona, como ya dicen muchas de las otras respuestas. No puede aplicar estilos a las clases dentro del SVG.
Frans

@Frans, ¿está incluyendo un svg como archivo o tiene su fuente de svg como en el ejemplo anterior? Porque tengo en mente que esto depende de cómo uses svg. Incluir por img no funcionará.
Dwza

Exactamente, solo funciona si inserta el SVG en su HTML. Pero eso no es lo que hace tu ejemplo. Utiliza un SVG externo (es decir, no en línea). Parece que no hay forma de diseñar un SVG externo con CSS en su HTML.
Frans

¿Seguro que has probado mi ejemplo correcto? Quiero decir, ¿incluyó un archivo css de un externo? <?xml-stylesheet href="stylesheets/main.css" type="text/css"?>
Dwza

Bien, ahora veo, tienes una <?xml-stylesheet ... ?>declaración dentro de tu SVG. Supongo que funcionaría. Es similar a otras respuestas que recomiendan un <link rel="stylesheet" ... >SVG interno. También tiene los mismos problemas (necesita actualizar cada SVG para que apunte a la hoja de estilo, y cualquier cambio en el nombre o ubicación de la hoja de estilo significa tener que cambiar todos los SVG nuevamente).
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: los estilos externos no funcionarán si incluye una <img>etiqueta interior SVG . Funcionará perfectamente dentro de la <div>etiqueta.


0

@leo aquí está la versión angularJS, gracias de nuevo

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 );

                } );

            } );
        }

    }

}

} );

-1

Este método funcionará si el svg se ve dentro de un navegador web, pero tan pronto como este código se carga en el servidor y la clase para el icono de svg se codifica como si fuera una imagen de fondo, el color se pierde y vuelve al color predeterminado. . Parece que el color no se puede cambiar desde la hoja de estilo externa aunque tanto la clase svg para el color como la clase de la capa superior para la visualización y posición del svg están asignadas al mismo directorio.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.