I have three projects: Proj A which contains a basic setup with files to be reused in many different projects, non editable. And Proj B and C, which uses the files from Proj A as its base. Each project has its own Gruntfile.
I want to customize some JS and SCSS files in Proj B and C, (e.g change colours/variables). But know that Proj A might be updated every now and then, so I can't edit any of those basic files as they would be overwritten when pulling the latest changes into B and C. I therefore have to create customised files which should override the files from Proj A.
1)
A way of doing it could be to copy the file I want to edit and simply add 'override' to it's filename:
ui/styles/sass/_override_colors.scss *(should override ui/styles/sass/_colors.scss)*
ui/scripts/_override_base.js (should override ui/scripts/base.js)
The problem then is: how do I specify this rule in my Gruntfile, "if any file starting with '_override' exists, is should override its equivalent file".
That might however not be optional if the file I copy is large, as it means two quite identiacall large files.
2)
One other approach could be to add a new file called _overrides.scss and add it in the very end in my #import file. That however would be a bad idea if I am to change a lot of things, as it might be too messy and big and also means duplicate code (unless I am only overriding variables and no classes).
Which approach would you go for? Are there any other better ways to solve this problem?
Thanks!
web.scss:
#import 'colors';
#import 'grid';
#import 'base';
// #import 'overrides'; ?
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
sass: {
dev: {
options: {
style: 'expanded',
sourcemap: true,
quiet: true
},
files: {
'styles/css/web.css': 'styles/sass/web.scss'
}
}
},
uglify: {
site: {
files: {
'scripts/min/site.js': [
'scripts/dev/forms.js'
'scripts/dev/base.js'
]
}
}
}, ....
I don't think anything should happen at the Gruntfile.js level, as this concerns only SASS.
If you prefer the override way, name the override files as _appB.scss and _appC.scss and add them to the compilation. These files won't get bloated as long as you only do overrides in them.
You might however decide to go the non-override way and create dedicated selectors for each app - which takes some time getting used to but will prove more maintainable in the long run. That way every selector lives a life of its own.
An override example:
_appA.scss (common styles)
.content { height: 80px; max-width: 980px; margin: 0 auto; }
_appB.scss
.content { height: 90px; }
_appC.scss
.content { height: 70px; max-width: 768px; }
markup
<div class="content"></div>
A block/element/modifier "override" example:
_appA.scss (common styles)
.content { height: 80px; max-width: 980px; margin: 0 auto; }
_appB.scss
.content--b { height: 90px; }
_appC.scss
.content--c { height: 70px; max-width: 768px; }
markup
<div class="content content--b"></div>
<div class="content content--c"></div>
The last markup example above is a representation of BEM syntax, which is a popular take on OOCSS.
Me and my colleagues at work maintain complex products and find this workflow very manageable.
Related
I want to have dynamic color scheme to my pages. Based on the url, I send an API request that fetches the color scheme from the database.
For this, I fetch the colors in the nuxtServerInit store hook. I then use v-bind() in CSS to dynamically color the components.
For reference, https://vuejs.org/api/sfc-css-features.html#css-modules.
Ex:
#main-container {
max-width: 500px;
margin: auto;
background-color: v-bind('designTemplate.backgroundColor');
}
Here, designTemplate is a computed variable that fetches the value from the store.
I am using SSG. On inspection of the generated files, I can see that the colors are not bound to css and the server rendered page does not have the required colors.
In the generated file, I am seeing
.class-name[data-v-15114cda]{background-color:var(--fec1f67e)}
But the css variable fec1f7e is not found in the file. Only during hydration is the colors actually applied.
Update:
Solved the problem. Used an alternate way of accomplishing my requirement.
Instead of using v-bind in css, I created root level css variables. This was done in the head section of my page.
head() {
return {
style: [
{
cssText: `
:root {
--base-color: ${this.designTemplate.baseColor};
--background-color: ${this.designTemplate.backgroundColor};
}
`,
type: 'text/css'
}
]
}
}
With this in place, I can access these variables in all my files and style them as:
#main-container {
max-width: 500px;
margin: auto;
background-color: v-bind('designTemplate.backgroundColor');
}
The css variables are injected in the head of the html and with server side generation, I get a properly colored server rendered page.
I am building a react-app, created with create-react-app, using bootstrap and react-bootstrap
I have a custom theme that is an npm package, and really just defines some variables.
#myorg/theme/lib/scss/_variables.scss:
$theme-colors: (
primary: #FFFF,
// etc...
)
Then I import this in my "site" theme before importing bootstrap:
./src/index.scss:
#import "~#myorg/theme/lib/scss/_variables.scss";
#import '~bootstrap/scss/bootstrap.scss';
Now I'd like to have components with their own specific styles, that build on bootstrap:
./src/components/MainLayout.scss:
#import "~bootstrap/scss/_functions";
#import "~bootstrap/scss/_variables";
#sidebar {
border-right: 1px solid $gray-400
}
However when I do this, it gets rid of my theme colors, so I have to do this -
./src/components/MainLayout.scss:
#import "../../index.scss";
#sidebar {
border-right: 1px solid $gray-400
}
Is this the correct way to do this? It seems to write the entire contents to the document (bootstrap and all) every time I do this, and I would be doing it a lot for many components. For instance, I have another component where I'd like to customize CardColumns (I actually can't get this to work no matter the import):
//#import ???? I need, functions, mixins, and variables from bootstrap
.card-columns {
#include media-breakpoint-only(lg) {
column-count: 10;
}
#include media-breakpoint-only(xl) {
column-count: 10;
}
}
What is the correct way to use bootstrap 4's scss files in modular react components? Without bloating the download size.
I would create 1 lib.scss file to contain all my import from library
like this name lib.scss
#import "~bootstrap/scss/bootstrap";
#import "~font-awesome/scss/font-awesome";
then when I create new module like admin module i will simply import like this
#import "./lib";
...
This questions is similar to some other on StackOverflow, but I couldn't find any answer describing applicable to my situation and non-deprecated method (and I'm starting thinking that maybe there is no any good solution for that situation).
Let's say we have some main.css file which includes common styles for buttons, lists, links and so on. So it's just some standard .css file which contains common styles that we want to reuse across the application. And we want to apply the same styles to Web Components with Shadow DOM.
There are a few ways, that I know about, to accomplish that:
Using one of deprecated approaches: ::shadow, >>>, /deep/ selectors. But those selectors are deprecated by now, so I guess it's not good approach to move forward with.
Using css variables. This approach is good for customization purposes, if we need to set a few properties. But it's too complex if we want to migrate 10-20 common styles from main.css file.
Using #import statement or "link" tags inside of Shadow DOM. It will work, but it will duplicate all styles for every component. If we have 10 web components we will end up with 10 duplicates of exactly the same styles. It doesn't sound like good enough solution too. Especially if we have a lot of common styles, sounds like it can be bad solution from performance point of view.
Don't use Shadow DOM at all and use global styles :) But it's not solution for current problem.
I also checked how the same problem resolved in Angular Framework (I checked version 5 of Angular). When I set encapsulation behavior to Native, it's just actually duplicating styles (like in #3 described above), what I think isn't the best way (but maybe the best currently existing way).
So, does anyone know if there is any other way to solve this problem without described above drawbacks? It just sounds like current drawbacks of Shadow DOM bring even more problems than it tries to solve.
There's no real drawback with solution 3:
Whether you apply a CSS style to n elements in a main document, or to 1 element in n Shadow DOM, the style will be duplicated to the whole n elements anyways.
If you import a document n times in n Shadow DOM, il will be actually be loaded only one time and reused through the browser cache.
After that, it will depend on the browser implementation of Shadow DOM and CSS styles, and you should see a performance degradation only the thousands of Shadow DOM.
2019 update for Chrome 73+ and Opera 60+
Now you can directly instanciate a CSSStyleSheet object and assign it to different Shadow DOMs.
This way the HTML won't be duplicated.
var css = new CSSStyleSheet()
css.replaceSync( "#import url( main.css )" )
host.shadowRoot.adoptedStyleSheets = [css]
host2.shadowRoot.adoptedStyleSheets = [css]
You can also apply it to the global document:
document.adoptedStyleSheets = [css]
The other advantage is that an update on the stylesheet will be applied to all Shadow DOMs (and document) that adopted it.
css.replaceSync( '.color { color: red }' )
I managed to do it using javascript modules but I doubt it's the cleanest solution.
You can create a GlobalStyles.js file that will contain the css styling that is common throughout various components. Changing the language mode on your editor to 'html' will provide syntax highlighting for the css.
const GlobalStyles = {
main: `
<style>
body {
overflow: hidden;
margin: 0;
font-family: 'Poppins';
}
h3 {
font-size: 39px;
}
</style>
`,
button: `
<style>
button {
display: block;
cursor: pointer;
outline: none;
font-family: 'Poppins Medium';
line-height: 17px;
padding: 9px 13px;
font-size: 15px;
background-color: #9f28d8;
color: white;
border: 2px solid;
border-radius: 5px;
border-color: #9f28d8;
width: max-content;
}
</style>
`
}
export default GlobalStyles;
Afterwards, you can import it into another js file that contains the code to the shadow dom of your custom element.
import GlobalStyles from './GlobalStyles.js';
const template = document.createElement('template');
template.innerHTML = `
${GlobalStyles.button}
<style>
ul {
font-family: Helvetica, Arial, sans-serif;
font-size: 13px;
width: 20em;
list-style-type: none;
}
</style>
<ul></ul>
<button>Click me</button>
`;
export class CustomList extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.appendChild(document.importNode(template.content, true));
}
}
The drawback with this approach is it only works if you are working purely with js files.
So I have two components... a Navbar component, and an AboutPage component.
They are both in the same directory, 'App'
App
-- Navbar --> Navbar.css, Navbar.js
-- AboutPage --> Aboutpage.css, Aboutpage.js
So as you can see, they have two separate stylesheets.
In the JS pages the correct CSS file is being imported as well.
When I do a style like this for example:
Navbar Component
p { background: red }
^^ this style also applies to the p's in the Aboutpage. I even tried to give the P in Aboutpage its on id and style it that way and it still failed.
That's the expected behaviour.
No matter which file you specify a rule like p { background: red }, it's going to be applied to all DOM.
Specifying and id attribute to won't work either. The above rule is general enough to apply to all <p>s.
If you want to specify css files for each component, you should also create component specific css classes. Like the following example.
import React from 'react';
import './DottedBox.css';
const DottedBox = () => (
<div className="DottedBox">
<p className="DottedBox_content">Get started with CSS styling</p>
</div>
);
export default DottedBox;
and its css file:
.DottedBox {
margin: 40px;
border: 5px dotted pink;
}
.DottedBox_content {
font-size: 15px;
text-align: center;
}
If you want different ways of defining css for React, this resource adds 3 more ways of doing so, in addition to the above way.
You can also use css modules. They scope your CSS locally and are awesome
Scoping styles to a component requires WebComponents which relies on several newer browser features, particularly shadowRoot "shadownDOM" which supports this separation directly. These are most easily used with lit-element and/or Polymer 3.
Sometimes we need a global CSS which could affect another component even if we use module import, I didn't find anything to answer that in the official documentation, so my workaround is to use something like the following code in the component itself, and, it works fine :)
<style>
{
`
#page {
padding:0;
margin-top:0;
margin-bottom: 0;
margin-left: 0;
margin-right:0;
}
#media print {
#page {
size: 80mm 21cm;
}
}
`
}
</style>
while developping an angular 4 app using angular-cli + sass, i have this problem with multiple plugins. so here is the scenario:
I have a main style.scss file that import other styles files ..., so if i want to modify a plugin style, let say a slider component styles, i will create a slider.scss and import it into my main style.scss file, the problem is that the imported component css is injected into the browser after my main css file. so when i want to modify something i need to add !important to the rule.
here is a snapshot of chrome inspect css tool:
element.style {
left: 100%;
}
<style>…</style>
.noUi-marker-horizontal.noUi-marker-large {
height: 15px;
}
<style>…</style>
.noUi-marker-horizontal.noUi-marker {
margin-left: -1px;
width: 2px;
height: 5px;
}
<style>…</style>
.noUi-marker-horizontal.noUi-marker-large {
height: 10px !important;
}
my custom css is the last one.
I want to know if there is a way to load my css style file after the components css file, i can use precedence rule to make it work, but i want to know if there is a more clean way.