Simplify matching elements in SCSS - sass

The HTML DOM structure I have is, sort of, repetitive. Is there any good practice to refactor my sass rules?
HTML:
<div name="OpportunityDetailView" class="actor-wrapper"><div name="OpportunityDetailView" class="detail-view expansion-bottom-normal">...</div></div>
<div name="ApplicationDetailView" class="actor-wrapper"><div name="ApplicationDetailView" class="detail-view expansion-bottom-normal">...</div></div>
<div name="ProfileDetailView" class="actor-wrapper"><div name="ProfileDetailView" class="detail-view expansion-bottom-normal">...</div></div>
SCSS case 1:
div[name="OpportunityDetailView"] > div[name="OpportunityDetailView"],
div[name="ApplicationDetailView"] > div[name="ApplicationDetailView"],
div[name="ProfileDetailView"] > div[name="ProfileDetailView"],
{
css rules...
}
SCSS case 2:
.section {
div[name="Business_Image_Url__c"],
div[name="Name"],
div[name="Account_Name__c"],
div[name="Business_Type__c"],
div[name="Region_Province__c"] {
.label{
display: none !important;
}
}

If I'm not missing something, it seems like you can use the classes assigned to the elements to achieve what you want:
For case 1:
.actor-wrapper {
.detail-view {
//css code goes here
}
}
and please provide some markup for the case 2.
If all the div[name] elements share the same class, then you can group them by that class, as done with the above example.

Related

nested selector with scss on active class

Sorry for the title, I don't know how to explain it.
I have an item like this :
<div class="faq__item">
<div class="faq__item-header"></div>
</div>
When I click on faq__item, I have an active class active
So in my scss, I want to style the header without selecting the whole child selector and keep the ampersand
.faq__item {
&.active{
.faq__item-header{ => I would like to avoid this
}
}
}
I don't know if I'm clear, I'm pretty sure I can optimise the code, but I don't know what to search
Store the parent selector in a variable and use it like so:
.faq__item {
$parent: &;
&.active {
#{$parent}-header {
/* rules here */
}
}
}

Vue3 css variable injection [duplicate]

I am making a css grid system that relies on the concept of blocks. So I have a base file like:
$max-columns: 4;
$block-width: 220px;
$block-height: 150px;
$block-margin: 10px;
And it is used by a mixin:
#mixin block ($rows, $columns, $max-columns) {
display: block;
float: left;
margin: $block-margin 0 0 $block-margin;
box-sizing: border-box;
width: ($block-width * $columns) - $block-margin;
}
But I'd also like javascript to have access to the variables in the base file. I was thinking that I could make an invisible div, and give it the $block-width, $block-height, and $block-margin attributes and pull the values from there. But max-columns, doesn't map to anything directly, so I'd have to come up with a hacky way to render it in a div. Is there a cleaner way to share values from sass/css to javascript or vice versa?
If you use webpack you can use sass-loader to exportvariables like:
$animation-length-ms: $animation-length + 0ms;
:export {
animationMillis: $animation-length-ms;
}
and import them like
import styles from '../styles/animation.scss'
const millis = parseInt(styles.animationMillis)
https://blog.bluematador.com/posts/how-to-share-variables-between-js-and-sass/
I consider my solution to be quite hokey; but it does work...
In my _base.scss I have some variables defined:
$menu_bg: rgb(45, 45, 45);
$menu_hover: rgb(0, 0, 0);
In a menu.scss I have:
#import "base";
#jquery_vars {
.menu_bg {
background-color: $menu_bg;
}
.menu_hover {
background-color: $menu_hover;
}
}
And in a handy page template:
<span class="is_hidden" id="jquery_vars">
<span class="is_hidden menu_bg"></span>
<span class="is_hidden menu_hover"></span>
</span>
Finally this allows in a nearby jQuery script:
var menu_bg = $('#jquery_vars .menu_bg').css("background-color");
var menu_hover = $('#jquery_vars .menu_hover').css("background-color");
This is so ugly my dad is wearing a bag on his head.
jQuery can pull arbitrary CSS values from page elements; but those elements have to exist. I did try pulling some of these values from raw CSS without creating the spans in the HTML and jQuery came up with undefined. Obviously, if these variables are assigned to "real" objects on your page, you don't really need the arbitrary #jquery_vars element. At the same time, one might forget that .sidebar-left nice-menu li is the vital element being use to feed variables to jQuery.
If someone has anything else, it's got to be cleaner than this...
sass-ffi should do the trick, but the opposite way (from JS to SASS/SCSS). It will define a function called ffi-require, which allows you to require .js files from SASS:
config.js:
module.exports = {
maxColumns: 4,
};
style.scss:
$max-columns: ffi-require('./config', 'maxColumns');
Works with sass-loader (webpack) and node-sass.
You can read the sass file with a server side script, "parse" it and echo the values you need to javascript.
I would like to add that there are now several ways to share data between Sass and JavaScript using JSON. Here are some links to articles detailing various techniques:
Making Sass talk to JavaScript with JSON
SassyJSON: Talk to the browser
Sharing Data Between Sass and JavaScript with JSON
It's probably just a matter of time until JSON importing becomes supported natively in Sass.
I would recommend looking at sass-extract which uses native sass features in order to extract the computed variable values into JSON.
Also if you are using webpack the sass-extract-loader will make it very easy to just require/import the sass files as in const variables = require('sass-extract-loader!./variables.scss'); and have your sass variables in a nice JSON object.
Since it also supports #import statements you can still separate your variables in different files, and no need to add additional preprocessing or separate json files with variables.
There are many alternative ways of accomplishing this as mentioned in other answers, and which one you choose will depend on your use case and environment.
Disclaimer, I am the author of both mentioned libraries.
Another way could be to use gulp-template so you can generate any structure you want for your JavaScript.
Sharing Variables between Javascript and Sass using Gulp with gulp-template
https://youtu.be/UVeUq8gMYco
It's created from scratch so people could see it from the ground up and there is a git repo with the end result:
https://github.com/PocketNinjaCoUk/shared-js-sass-vars-using-gulp/tree/master/dev
You basically have your config object
saved at ./dev/config.js
module.exports = {
defaults: {
colours: {
primary: '#fc0'
},
sizes: {
small: '100px',
medium: '500px',
large: '1000px'
},
zIndex: {
model: 100,
dropdown: 50,
header: 10
}
}
}
Then you have both of your templates for Sass and Javascript, or less or whatever you want.
Sass underscore template
saved at ./dev/templates/sass-config.txt
<% _.each(defaults, function(category, key) { %>
// Var <%= key %>
<% _.each(category, function(value, key) { %>
$<%= key %>: <%= value %>;
<% }) %>
<% }) %>
Javascript underscore template
saved at ./dev/templates/js-config.txt
namespace.config = {};
<% _.each(defaults, function(monkey, key) { %>
namespace.config.<%= key %> = {
<% i = 1 %>
<% _.each(monkey, function(value, key) { %>
<% comma = (Object.keys(monkey).length === i) ? '': ',' %>
<% if(typeof value === 'string') {%>
<%= key %>: '<%= value %>'<%= comma %>
<%} else { %>
<%= key %> : <%= value %><%= comma %>
<% } %>
<% i++ %>
<% }); %>
};
<% }) %>
Then the gulp to compile it
var gulp = require('gulp');
var template = require('gulp-template');
var rename = require('gulp-rename');
var removeEmptyLines = require('gulp-remove-empty-lines');
var sharedVars = require('./dev/config');
gulp.task('compile', function() {
gulp.src('./dev/templates/sass-config.txt')
.pipe(template(sharedVars))
.pipe(rename('_sass-config.scss'))
.pipe(removeEmptyLines())
.pipe(gulp.dest('./dev/sass'));
gulp.src('./dev/templates/js-config.txt')
.pipe(template(sharedVars))
.pipe(rename('js-config.js'))
.pipe(removeEmptyLines())
.pipe(gulp.dest('./dev/js'));
});
This can be done using gulp-sass-vars-to-js. It generates a .js file from your .scss file. The .js file contains all variables declared in your .scss file. You can then 'require' this generated js into your .js

Skeleton page with v-cloak: how to target loading div with css ? Vue

I'm using Webpack/Laravel and injecting vuejs on id #app in my page so to have skeleton loading page I want to have this markup
//client side, will show after ~0.2 seconds
<div id="app" v-cloak>
<hello-world>
</div
//server side, show for ~0.2 s then disappear
<div id="app__loading" v-cloak>
<div class="skeleton">
<span class="skeleton_ribs"></span>
</div>
</div>
I managed to display loading gif in background as pseudo element when v-cloak and everything inside from #app__loading is removed, but if I add normal elements in markup they appear at the bottom of the page after #app loads
[v-cloak] > * { display:none }
[v-cloak] + #app__loading::before {
content: url('https://i.pinimg.com/originals/65/ba/48/65ba488626025cff82f091336fbf94bb.gif');
display: flex;
justify-content: center;
align-items: center;
}
But I would like something like this to work with markup inside #app__loading, but it doesn't work
[v-cloak] > * { display:none }
#app-loader{
display: block;
}
[v-cloak] #app::finish-loading{
[v-cloak] #app-loader{
display: none!important;
}
}
You seem to be expecting the content your initial element to be found in the template of app root element. When using $mount() function, Vue completely replaces the target element with the template of the mounted element.
This replacement is somewhat masked by the fact that, out of the box, the index.html contains a <div id="app"> which gets replaced by the <div id="app">...</div> in your App.vue template.
But they're actually not related (and not the same element). If you changed the id of the one in index.html you'd need to change the target el in main.(js|ts). And obviously, you could change the id of the one in App.vue as well.
Now, to solve your issue, you could simply place v-cloak on the div in your App.vue. From what you've shown so far, that should make it work as expected.
For more complex use cases (i.e: if you wanted to trigger a particular event bound to the initial element) the way to go would be to wrap the initial placeholder in a parent, bind to the parent and, later on, in Vue, target the parent with this.$el.closest('.some-class') or with this.$el.parentElement() to access the binding.
If you still can't get the expected result, please create a mcve (you could use codesanbox.io) and describe in detail what is the expected behavior.
To make your headache smaller till you find proper vue solution, you can grab loader by id and when Vue instance mounts - give display none
export default {
mounted() {
document.querySelector('#app__loading').style.display = 'none';
},
}

Can wkhtml2pdf support CSS counters?

I have counter-reset and counter-increment working fine in HTML, but they are not appearing in the rendered PDF. Are they supported? (An hour of Googling didn't help.)
Here is my CSS. Again, this works fine in HTML, but does not appear in the PDF.
body {
counter-reset: h3counter 1;
counter-reset: h4counter 1;
}
h2 {
counter-reset: h3counter;
}
h3 {
counter-increment: h3counter;
counter-reset: h4counter;
}
h3:before {
content: counter(h3counter) ".\0000a0\0000a0";
}
h4 {
counter-increment: h4counter;
}
h4:before {
content: counter(h3counter) "." counter(h4counter) ".\0000a0\0000a0";
}
Yes, it works (in wkhtmltox.dll version 0.11.0.0), but only in simple cases. I just ran into the problem that the counter seems to reset when the heading tags (h1, h2, h3) are not direct siblings - i.e., each is wrapped in a div. When the headings are adjacent, as in:
<h2>..</h2>
<h3>...</h3>
<h3>...</h3>
wkhtmltopdf numbers correctly; in this case:
<h2>...</h2>
<div>
<h3>...</h3>
</div>
<div>
<h3>...</h3>
</div>
it does NOT. The two h3's get the same number.

Susy reusable responsive columns

I'm trying to wrap my head around Susy. I think I'm going to like it. Just need to work with it more. I'm basically trying to create reusable columns that I can use throughout a site. I'm coming from using the Foundation grid, so maybe I'm not thinking about this right?
I'd need to be able to target those columns. I've read a few articles that say we shouldn't be filling our divs with classes like column-2 or small-6. I guess I'm not seeing how you target the divs if I don't tell it what I expect.
The code below works, but is it very Susy? Is it the right approach? I'd have to create similar rules for all 12 column widths. I'd have to decide up front how I want those columns to break. Do I want the span 6 columns to span 6 all the way down to medium or should they change to span 12. These rules would have to be determined up front.
If this is even the right approach. Any help, guidance or pointers is appreciated.
HTML
<div class="row">
<div class="column-6">
<div class="column-2"></div>
<div class="column-2"></div>
<div class="column-2"></div>
<div class="column-2"></div>
<div class="column-2"></div>
<div class="column-2"></div>
</div>
<div class="large-6">
<!-- Large image goes here -->
</div>
</div>
SUSY SASS
$susy: (
columns: 12,
gutters: 1/4,
container: 64em,
global-box-sizing: border-box,
);
$medium: 30em;
$large: 64em;
.column-2 {
#include span(12 last);
#include breakpoint($medium) {
#include span(6);
&:nth-child(2n) {
#include last;
}
}
#include breakpoint($large) {
#include span(2);
&:nth-child(2n) {
#include span(2);
}
&:last-child {
#include last;
}
}
}
.column-6 {
#include span(12 last);
#include breakpoint($medium) {
#include span(12);
&:nth-child(2n) {
#include last;
}
}
}
You've got the right idea. Susy is different from Foundation and Bootstrap in that we declare the responsiveness in our Sass instead of filling the html with class like "large-3 pull.."
I usually name my classes something more meaningful that "column-6" like "main-content". Other than that it looks like the right approach.
#include span(12 last);
You don't really need to specify last here since it's spanning the full width.
#include full;
Would probably be more appropriate, or just
#include span();
Which will default to your columns defined in the settings.

Resources