How to reference a Sass stylesheet within SVG in a React App - css

For background, I am trying to load SVG icons in my react app and am using the object tag so that I can manipulate the color of the icons easily.
In this guide, they say:
If you want to use external styles, which are mostly much easier to work with and maintain, you can’t use or background-image. If you are using you need to reference your stylesheet internally from the SVG file (see code following). Remember: if you do this the SVG will also not be able to know what its parent class is (i.e. the ) so don’t try to use that in its styling. Inline SVGs don’t need this added and therefore can be slightly easier to work with in this sense.
and provide this code example:
// Add to very start of SVG file before <svg>
<?xml-stylesheet type="text/css" href="style.css"?>
// In style.css
.firstb { fill: yellow; }
.secondb { fill: red; }
To give you an idea of how my files are organized, my src folder contains an assets folder along with my components folder. My style.scss file lives within the src folder as well.
I'm having trouble figuring out what to add to the top of my svg files. My understanding of sass is that it would usually compile style.scss to style.css, however, when I look at my sources in dev tools it shows that style.scss is being loaded directly. Is this just because I'm still in development mode?
Anyways, I've tried the following (none of which have worked):
<?xml-stylesheet type="text/css" href="./style.scss"?>
<?xml-stylesheet type="text/css" href="style.css"?>
<?xml-stylesheet type="text/css" href="style.scss"?>
It would be a huge help if anyone knows how I can get this to work! Let me know if there's any other information I can provide. Thanks so much!

You are correct to assume that you'd need to compile style.scss to style.css, so first ensure that only the CSS is being referenced in the SVG. This is the most straightforward way to get this to work, but a better solution would be to import your styles once in your root HTML template instead.
Now, if you're using React it's likely that you're also using a bundler such as webpack. You can tell bundlers like webpack to handle non-JS imports such as Sass files by using loaders. There is a sass-loader for webpack that will allow you to import Sass files, however you may also need to add the svg-inline-loader so that you can import the SVG directly.
I think with both loaders, what will happen is that webpack will start bundling your React app, see that an SVG is imported, grab it and see that it is referencing a Sass file, grab the sass file and compile it, and then output the styles in your index.html file. Please let me know if this solution works for you!

I found a workaround. Not exactly what I was hoping for, but it works for me.
There's a react-svg package that handles an ajax request and pulls the svg in as a normal svg tag. This allows me to edit with my sass file.
https://www.npmjs.com/package/react-svg

Related

Questions about CSS bundling in Rails 7

Rails 7. New App with Bootstrap CSS, JS Bundling, and CSS Bundling. This results in structure of:
app/assets/builds,
app/images/foo.jpg,
app/assets/stylesheets/application.bootstrap.scss
and using Yarn to add Bootstrap to package.json, with ESBuild for JS build and Sass for CSS build.
It all works until I try add a simple CSS class to the application.bootstrap.scss sass file:
.bg {
background-image: url("foo.com");
}
What I really want here, is the asset in app/assets/images/foo.jpg to be referenced properly. It is a scss file, so sass. When I use the sass commands:
background-image: image-url("foo.com");
or
background-image: url(image-path("foo.com"));
nothing works in development, or production, so that my application.css build file is correct. I get errors about syntax ending in a "we found a .jpg but should be (1px 0 solid) or something like that. In other words, the sass compile is not making a valid css build.
What am I supposed to be doing here to make a simple class with an image asset be part of my delivered application.css bundle? Should I be creating a separate css file and adding that to the sprockets manifest? Seems like overkill.
At the moment the only thing that worked was just adding an inline style to my ERB layout, which is totally bogus bad, but all I could do to just move on.
As an extra question, which I know I should not ask here, I also want to reference an image I add to the Rails App, so that https://myapp.com/my-image.png is just available. I used to park this in /public/my-image.png but if I wanted to instead use /app/assets/images/my-image.jpg what would my link be? I guess it would be all fingerprinted and not accessible, but perhaps I am wrong. Is there any point to referencing an asset instead of parking it in /public or am I forced to use /public?
see
https://github.com/rails/cssbundling-rails/issues/102
did you try
.test {
background-image: url("foo.jpg");
}
https://github.com/rails/sprockets-rails#initializer-options
config.assets.resolve_assets_in_css_urls
When this option is enabled, sprockets-rails will register a CSS postprocessor to resolve assets referenced in url() function calls and replace them with the digested paths. Defaults to true.

How to import part of an scss file

For example I am trying to import .navbar-nav from bootstrap's _navbar.scss and not the whole _navbar.scss file to my compiled css file. Is there a way to do it?
Sorry if this was asked before.
You can try doing an extend:
.your-class{
#extend .navbar-nav;
}
However, this would only work if you had imported the _navbar.scss somewhere else or the bootstrap.scss.
Additional
// main.scss
#import ../wherever bootstrap file is/_navbar.scss;
#import _custom.scss;
// _custom.scss
.your-class{
#extend .navbar-nav;
}
One of the way to import .scss in javascript is
import { navbar-nav } from '_navbar.scss'
When using in your component you can do.
<div className={navbar-nav} />
if you want to import it in your .scss file then you can do.
#import '_navbar.scss'
.class {
#extend .navbar-nav
}
As you are learning Sass here are some explanations which may help:
Better wording helps ...
At first some wording to get a correct understandable communication here and anywhere else you are talking about coding:
SASS don't minify a given CSS, it writes the CSS. Minify means the process that a given CSS code is compressed by a postprocessor to a shorter way to write it, - i.e. comments and spaces will be removed ... But yes: as SASS writes CSS it is able to write code in a minified format.
What you mean is to 'reduce code' or 'avoid not needed code' as you only try to import, use and write! the only needed parts of a given module which is a good practice.
.navbar is a CSS class. SASS don't load CSS classes, it writes CSS classes. It doesn't matter if you 'write the code on your own to a SCSS file' or 'get the code from a framework/module' ... SASS writes the however prepared CSS classes to your CSS file.
What you mean is the SASS includes/imports files with code from a framework/module to write that code/classes to css. So yes: maybe you can say you 'load' that module/scss-file ... but you don't load as css class. (This is as important as 'classes' in coding allways means a special construct of excutable code which does something in your programm. CSS classes don't execute anything, in SASS they are content you want to write/output to css.)
Please: these wordings are important to understand each other and to understand the mechanic of the process how SASS works is going on as well.
Reducing code by importing only selected file is good practice
So, I am not sure if I did understand your question right:
No. You are not able to include/import/load a part of the code of a single scss-file only. If you do #import 'somefile.scss' you always get the whole code of the whole file.
Yes. you are able to include/import/load parts of a given framework/module as you are able to load only the special FILES(!) of a framework/module you need for your project.
Yes. That is a really good practice.
As you mentioned Bootstrap indeed is developed and allows you to do that. But head up. If you import i.e. the part navbar.scss (or other selected elements) it only works if you also load the other files navbar.scss depends on. That are almost variables, functions, mixins and sometimes needed JS components to this element as well. Please note, that importing the files the elements are based on (i.e. vars, functions, mixins) has to be done BEFORE you load the element (i.e. like navbars, grid,...) itself.
A way to organize your project
Yes. A good way to organize your project is to have a single(!!!) file which brings all the code together you write in other partial files yourself or which you import from other framework/modules.
In case of Bootstrap this can be (simplified example):
// ###> file: your 'custom.scss'
// Note: file is without leading underscore
// as this files GENERATES/WRITE the css to custom.css
// Files with underscore as _partial-footer-styling.scss
// are not compiled to write css on their own
// that files are only compiled to css when they are imported to files without underscore
#import 'path/your-own-vars';
// Note: technique importing files
// you don't need to write underscore and '.scss'
// Note: function of this file
// the file '_your-own-vars.scss' is to organize you needed vars special to your project
// it includes your own vars and bootstrap vars as well
// --> the Bootstrap vars in this file will overwrite the vars of Bootstrap which will be included next
#import 'bootstrap-path/functions';
#import 'bootstrap-path/variables';
#import 'bootstrap-path/mixins';
#import 'bootstrap-path/your-selected-component-1';
#import 'bootstrap-path/your-selected-component-2';
#import 'bootstrap-path/your-selected-component-3';
...
#import 'path/partial-your-own-additional-css-special-section';
#import 'path/partial-your-own-additional-css-footer-settings';
....
A detailed explanation how to include and use Bootstrap (partly if you like to do so) to your project is here: https://getbootstrap.com/docs/4.6/getting-started/theming/

Having issues while importing whole sccs file into a wrapped selector

I was looking for an easy way to prefix a style sheet and sass works just great. I don't need any building tool, just vs code sass extension, and press watch.
What I did was, renamed the css to scss and then imported it inside the main style nesting in the selector I want, like:
#wrapper {
#import 'style1';
#import 'style2';
}
The issue comes when one of the files has #font-face, they also get prefixed and that is a problem. I checked the issue tracker and apparently this is the correct behavior.
https://github.com/sass/sass/issues/2442
Given that. I am looking for a way to import only the #font-face rules to the root instead of the #wrapper selector.
Is this possible without having to change the content of 'style1' or 'style2' ?
I was able to get around this problem with node sass magic importer.
But again you need node scripting and terminal, but can be mitigated with a bundler which kinda is not what I want but at least I can sort of prebuilt it and still use a watcher.
But given the hasle to set this up for such a simple thing I would just go to the style sheet and copy the font-faces to the root of the main file anyways.
If anyone knows a solution with sass only please reply.

Global styling vs local styling in VueJS

I'm building a project with .vue files which make it possible to write the CSS (SASS), JS and HTML in the same file.
I've decided to have some global components written in SASS on a assets/styles/app.scss file which will load my grid, variables and mixins.
On top of that, I want to be able to write some local SASS rules depending the component / page I'm on, seems pretty logical to want both in a project ...
Locally it looks like this:
<template>
</template>
<script>
</script>
<style lang="scss">
#import "assets/styles/app";
.my-style {
color: $my-variable;
}
</style>
It actually works, for instance I can use $my-variable in my local .vue file or any mixin I want. The problem is a VueJS project will grow and components will go together to display a page.
I noticed the global styling was loaded on each component, and the same rule is present in 5x, 10x when I open my chrome developer tool. This is still a very small project; all my styles are basically duplicated and loaded by the browser each time I add a component to the same page.
How do you avoid to load multiple times the global styles, while being able to use global SASS code in each components?
I've never worked with local mixed with global styling before, I preferred to just abstract totally the styling into a separated structure, but this is way more convenient to code with everything local in the same place.
What am I doing wrong here?
Detail: I'm on NuxtJS but I believe this issue is more related to VueJS overall.
Basically, every time you do an #import in your components it appends another copy to the main CSS file that Webpack generates.
Assuming you have the Webpack SCSS loader properly configured (which I believe you do since it compiles), you should be able to import the SCSS file once in your app.vue and the SCSS compiler will find it when it appends all other CSS.
For example, getting global fonts and mixins:
<style lang="scss">
#import url('https://fonts.googleapis.com/css?family=Lato:300,400,400i,700,900&subset=latin-ext');
#import "#/scss/mixins.scss";
</style>
Then create your CSS for each component inside the component's <style> section. Just make sure you add the lang="scss" so it all compiles.
You might also want to look into scss-resource-loader for Webpack. I think this is in the newest CLI builds, not sure about Nuxt.
in App.vue
<style lang="scss">
#import "assets/styles/common.scss";
</style>
OR
import compiled sass to css file in main.js
import './assets/styles/common.css';

Keeping CSS out of JS in Angular 2/Angular-CLI

By default, Angular 2 compiles the CSS into JavaScript, especially when using WebPack as in Angular-CLI. I would rather this not happen for a couple of reasons.
The first reason is that when I'm developing, I find it really helps to be able to see in the developer tools exactly what style sheet a specific style rule was coming from and what line number it was on. The second reason is that I think compiling CSS into the code kind of misses the point of good CSS, which is that you can apply a different style sheet and have an entirely different look and feel with the same markup.
Is there a flag somewhere that I can set to leave the CSS in .css files, where IMO it belongs?
This is the whole point of encapsulated components.
A component should have it's own styles encapsulated with it so it can be shipped with the styles.
Imagine you want to publish one of your components to be used by others, shouldn't it have its own styles with it ?
That means Angular needs a way to link those css to the component , thus seperates them into chunks and injects them into head tag.
To solve your problem though , you have couple of options :
1- Not using the Emulated Encapsulation :
Components by default have a property called encapsulation which is set to Emulated , you need to change it to None:
#Component({
encapsulation:ViewEncapsulation.None
})
Then , you can put all you css in the head tag your self like you'd do with a normal html page.
2- If the problem is theme ing , you can make your component themeable .
You can have a theme attribute for your component and then based on that change the styleing :
#Component({
selector:'my-component',
styles:[
`
:host{
[theme="blue"]{
change what ever you want :
h1{
color:blue;
}
}
}
`
]
})
And then , using this component would be like :
<my-component [attr.theme]='"blue"'></my-component> // would be blue theme
<my-component></my-component> // would be default
Go to your base Html file(where the root module, main app is injected) and link the CSS stylesheets in your header section.
Webpack will not include it in it's compiled/combined css file which is injected into the page. The css file will still be included at run time in the browser.
<html>
<head>
<base href="/">
<meta charset="utf-8">
<title>dummy</title>
<meta name="viewport" content="width=device-width">
//was not injected/modified by webpack
<link rel="apple-touch-icon" sizes="57x57" href="app/images/apple-icon-57x57.png">
//webpack's injected this below from other components's imported/inline css rules
<link href="index-c2cacb5fa3dfbca6116f4e4e63d5c3c7.css" rel="stylesheet"></head>
With angular-cli 1.6.5 you can do this:
ng serve --extract-css
You will still have the style-encapsulation features, but devtools will now point to the component css source file.
I use the angular-cli as well (v1.0.0-beta.22). When I am ready to build for production I run the following command:
ng build -prod -aot
This generates all my production-ready files (bundled, tree-shaken and minified etc). Of particular note is that it will generate two versions of the style sheets.
One in js:
styles.b2328beb0372c051d06d.bundle.js
And another version is plain css:
styles.4cec2bc5d44c66b4929ab2bb9c4d8efa.bundle.css
I run some post-processing on the css file with gulp and use the css version for my production build. I am not sure if the same holds true for lazy loading (where the cli will produced different chunks), but it works for sure when lazy loading is not being used (I haven't launched a production-ready project yet with lazy loading).
I also tried a build with JiT compilation:
ng build -prod
It also produced the raw/minified version of the css style sheet.
Now, I know for sure the folowing does NOT work:
ng build
This will produce all the css embedded within js file, styles.bundle.js.
Since you want to use the raw css file during development, the only workaround I can think of is that you run ng build -prod in order to get the css file. Copy/paste this manually into your assets folder. Run "format" on the file to un-minify the file. Then do a normal build with a modified index.html file referencing your raw css file, and removing the styles.bundle.js script reference. Not pretty, but it might work.
Put a wrapper class in html example-
<div class="component-1-wrapper">
all yout html here inside component-1-wrapper
</div>
Structure your sass(scss) in the following way. Since your styles are wrapped inside component-1-wrapper, therefore it will apply only to component-1-wrapperclass
.component-1-wrapper{
// all the styles for component-1 here
.class-hello{
// styles
}
}
You can compile your css with sass and put all the css(seperated by modules) in seperate folder.Start the filenames by _, sass can import them:
You can refer your styles-main.scss in app.ts file
#component({
styleUrls:['styles/styles-main.scss']})
The style-sheets will be structured this way and individual component's class styles will be applied to particular component since there is a wrapper class in html
Hope it helps!!!!!!

Resources