adding cleanslate support for style scoping

This commit is contained in:
Benjamin Boudreau 2018-06-09 08:43:18 -04:00
parent 4e01bb3fd6
commit 81517491d5
9 changed files with 1045 additions and 384 deletions

921
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -53,7 +53,9 @@
"babel-loader": "^7.1.4",
"babel-preset-airbnb": "^2.4.0",
"babel-preset-stage-2": "^6.24.1",
"clean-webpack-plugin": "^0.1.19",
"css-loader": "^0.28.11",
"cssimportant-loader": "^0.4.0",
"enzyme": "^3.3.0",
"enzyme-adapter-react-16": "^1.1.1",
"enzyme-to-json": "^3.3.4",
@ -68,6 +70,8 @@
"jest-cli": "^23.0.1",
"mini-css-extract-plugin": "^0.4.0",
"node-sass": "^4.9.0",
"postcss-increase-specificity": "^0.6.0",
"postcss-loader": "^2.1.5",
"sass-loader": "^7.0.1",
"style-loader": "^0.21.0",
"webpack": "^4.8.3",

View File

@ -4,7 +4,7 @@
<title>Embeddable Widget</title>
</head>
<body>
<script src="./embeddable-widget.js"></script>
<script src="./widget.js"></script>
<script>
EmbeddableWidget.mount();
</script>

View File

@ -7,7 +7,7 @@
<h1>Embeddable Widget documentation</h1>
<ul>
<li><a href="./blank.html">Widget on a blank page</a></li>
<li><a href="./embeddable-widget.js">Latest version of the widget</a></li>
<li><a href="./widget.js">Latest version of the widget</a></li>
<li><a id="bookmarklet">Bookmarklet (Bookmark it for easy use)</a></li>
<li><a href="./bookmarklet.js">Bookmarklet Code</a></li>
<script>

View File

@ -1,6 +1,7 @@
@keyframes slideInUp {
from {
transform: translate3d(0, 100%, 0);
display: inherit;
visibility: visible;
}
@ -15,6 +16,7 @@
}
to {
display: none;
visibility: hidden;
transform: translate3d(0, 100%, 0);
}
@ -25,6 +27,7 @@
bottom: 0px;
right: 10px;
width: 200px;
z-index: 9999;
}
.dock {
@ -35,6 +38,7 @@
padding: 10px;
width: 180px;
border: 1px solid grey;
background: white;
}
@ -49,12 +53,14 @@
animation-name: slideInUp;
}
&-entered {
display: inherit;
visibility: visible;
}
&-exiting {
animation-name: slideOutDown;
}
&-exited {
display: none;
visibility: hidden;
}

View File

@ -1,12 +1,10 @@
import React from 'react';
import ReactDOM from 'react-dom';
import Widget from '../components/widget';
import EmbeddableWidget from './embeddable-widget';
const component = <Widget />;
const el = document.createElement('div');
document.body.appendChild(el);
console.log('running bookmarklet');
ReactDOM.render(
component,
el,
);
(function bookmarklet() {
if (window.EmbeddableWidget) {
return;
}
window.EmbeddableWidget = EmbeddableWidget;
EmbeddableWidget.mount();
}());

View File

@ -1,6 +1,7 @@
import React from 'react';
import ReactDOM from 'react-dom';
import Widget from '../components/widget';
import '../../vendor/cleanslate.css';
export default class EmbeddableWidget {
static el;
@ -13,6 +14,7 @@ export default class EmbeddableWidget {
throw new Error('EmbeddableWidget is already mounted, unmount first');
}
const el = document.createElement('div');
el.setAttribute('class', 'cleanslate');
document.body.appendChild(el);
ReactDOM.render(
component,

453
vendor/cleanslate.css vendored Normal file
View File

@ -0,0 +1,453 @@
/*!
* CleanSlate
* github.com/premasagar/cleanslate
*
*//*
An extreme CSS reset stylesheet, for normalising the styling of a container element and its children.
by Premasagar Rose
dharmafly.com
license
opensource.org/licenses/mit-license.php
**
v0.10.1
*/
/* == BLANKET RESET RULES == */
/* HTML 4.01 */
.cleanslate, .cleanslate h1, .cleanslate h2, .cleanslate h3, .cleanslate h4, .cleanslate h5, .cleanslate h6, .cleanslate p, .cleanslate td, .cleanslate dl, .cleanslate tr, .cleanslate dt, .cleanslate ol, .cleanslate form, .cleanslate select, .cleanslate option, .cleanslate pre, .cleanslate div, .cleanslate table, .cleanslate th, .cleanslate tbody, .cleanslate tfoot, .cleanslate caption, .cleanslate thead, .cleanslate ul, .cleanslate li, .cleanslate address, .cleanslate blockquote, .cleanslate dd, .cleanslate fieldset, .cleanslate li, .cleanslate iframe, .cleanslate strong, .cleanslate legend, .cleanslate em, .cleanslate summary, .cleanslate cite, .cleanslate span, .cleanslate input, .cleanslate sup, .cleanslate label, .cleanslate dfn, .cleanslate object, .cleanslate big, .cleanslate q, .cleanslate samp, .cleanslate acronym, .cleanslate small, .cleanslate img, .cleanslate strike, .cleanslate code, .cleanslate sub, .cleanslate ins, .cleanslate textarea, .cleanslate button, .cleanslate var, .cleanslate a, .cleanslate abbr, .cleanslate applet, .cleanslate del, .cleanslate kbd, .cleanslate tt, .cleanslate b, .cleanslate i, .cleanslate hr,
/* HTML5 - Sept 2013 taken from MDN https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/HTML5_element_list */
.cleanslate article, .cleanslate aside, .cleanslate figure, .cleanslate figcaption, .cleanslate footer, .cleanslate header, .cleanslate menu, .cleanslate nav, .cleanslate section, .cleanslate time, .cleanslate mark, .cleanslate audio, .cleanslate video, .cleanslate abbr, .cleanslate address, .cleanslate area, .cleanslate blockquote, .cleanslate canvas, .cleanslate caption, .cleanslate cite, .cleanslate code, .cleanslate colgroup, .cleanslate col, .cleanslate datalist, .cleanslate fieldset, .cleanslate main, .cleanslate map, .cleanslate meta, .cleanslate optgroup, .cleanslate output, .cleanslate progress, .cleanslate svg {
background-attachment:scroll !important;
background-color:transparent !important;
background-image:none !important; /* This rule affects the use of pngfix JavaScript http://dillerdesign.com/experiment/DD_BelatedPNG for IE6, which is used to force the browser to recognise alpha-transparent PNGs files that replace the IE6 lack of PNG transparency. (The rule overrides the VML image that is used to replace the given CSS background-image). If you don't know what that means, then you probably haven't used the pngfix script, and this comment may be ignored :) */
background-position:0 0 !important;
background-repeat:repeat !important;
border-color:black !important;
border-color:currentColor !important; /* `border-color` should match font color. Modern browsers (incl. IE9) allow the use of "currentColor" to match the current font 'color' value <http://www.w3.org/TR/css3-color/#currentcolor>. For older browsers, a default of 'black' is given before this rule. Guideline to support older browsers: if you haven't already declared a border-color for an element, be sure to do so, e.g. when you first declare the border-width. */
border-radius:0 !important;
border-style:none !important;
border-width:medium !important;
bottom:auto !important;
clear:none !important;
clip:auto !important;
color:inherit !important;
counter-increment:none !important;
counter-reset:none !important;
cursor:auto !important;
direction:inherit !important;
display:inline !important;
float:none !important;
font-family: inherit !important; /* As with other inherit values, this needs to be set on the root container element */
font-size: inherit !important;
font-style:inherit !important;
font-variant:normal !important;
font-weight:inherit !important;
height:auto !important;
left:auto !important;
letter-spacing:normal !important;
line-height:inherit !important;
list-style-type: inherit !important; /* Could set list-style-type to none */
list-style-position: outside !important;
list-style-image: none !important;
margin:0 !important;
max-height:none !important;
max-width:none !important;
min-height:0 !important;
min-width:0 !important;
opacity:1;
outline:invert none medium !important;
overflow:visible !important;
padding:0 !important;
position:static !important;
quotes: "" "" !important;
right:auto !important;
table-layout:auto !important;
text-align:inherit !important;
text-decoration:inherit !important;
text-indent:0 !important;
text-transform:none !important;
top:auto !important;
unicode-bidi:normal !important;
vertical-align:baseline !important;
visibility:inherit !important;
white-space:normal !important;
width:auto !important;
word-spacing:normal !important;
z-index:auto !important;
/* CSS3 */
/* Including all prefixes according to http://caniuse.com/ */
/* CSS Animations don't cascade, so don't require resetting */
-webkit-background-origin: padding-box !important;
background-origin: padding-box !important;
-webkit-background-clip: border-box !important;
background-clip: border-box !important;
-webkit-background-size: auto !important;
-moz-background-size: auto !important;
background-size: auto !important;
-webkit-border-image: none !important;
-moz-border-image: none !important;
-o-border-image: none !important;
border-image: none !important;
-webkit-border-radius:0 !important;
-moz-border-radius:0 !important;
border-radius: 0 !important;
-webkit-box-shadow: none !important;
box-shadow: none !important;
-webkit-box-sizing: content-box !important;
-moz-box-sizing: content-box !important;
box-sizing: content-box !important;
-webkit-column-count: auto !important;
-moz-column-count: auto !important;
column-count: auto !important;
-webkit-column-gap: normal !important;
-moz-column-gap: normal !important;
column-gap: normal !important;
-webkit-column-rule: medium none black !important;
-moz-column-rule: medium none black !important;
column-rule: medium none black !important;
-webkit-column-span: 1 !important;
-moz-column-span: 1 !important; /* doesn't exist yet but probably will */
column-span: 1 !important;
-webkit-column-width: auto !important;
-moz-column-width: auto !important;
column-width: auto !important;
font-feature-settings: normal !important;
overflow-x: visible !important;
overflow-y: visible !important;
-webkit-hyphens: manual !important;
-moz-hyphens: manual !important;
hyphens: manual !important;
-webkit-perspective: none !important;
-moz-perspective: none !important;
-ms-perspective: none !important;
-o-perspective: none !important;
perspective: none !important;
-webkit-perspective-origin: 50% 50% !important;
-moz-perspective-origin: 50% 50% !important;
-ms-perspective-origin: 50% 50% !important;
-o-perspective-origin: 50% 50% !important;
perspective-origin: 50% 50% !important;
-webkit-backface-visibility: visible !important;
-moz-backface-visibility: visible !important;
-ms-backface-visibility: visible !important;
-o-backface-visibility: visible !important;
backface-visibility: visible !important;
text-shadow: none !important;
-webkit-transition: all 0s ease 0s !important;
transition: all 0s ease 0s !important;
-webkit-transform: none !important;
-moz-transform: none !important;
-ms-transform: none !important;
-o-transform: none !important;
transform: none !important;
-webkit-transform-origin: 50% 50% !important;
-moz-transform-origin: 50% 50% !important;
-ms-transform-origin: 50% 50% !important;
-o-transform-origin: 50% 50% !important;
transform-origin: 50% 50% !important;
-webkit-transform-style: flat !important;
-moz-transform-style: flat !important;
-ms-transform-style: flat !important;
-o-transform-style: flat !important;
transform-style: flat !important;
word-break: normal !important;
}
/* == BLOCK-LEVEL == */
/* Actually, some of these should be inline-block and other values, but block works fine (TODO: rigorously verify this) */
/* HTML 4.01 */
.cleanslate, .cleanslate h3, .cleanslate h5, .cleanslate p, .cleanslate h1, .cleanslate dl, .cleanslate dt, .cleanslate h6, .cleanslate ol, .cleanslate form, .cleanslate option, .cleanslate pre, .cleanslate div, .cleanslate h2, .cleanslate caption, .cleanslate h4, .cleanslate ul, .cleanslate address, .cleanslate blockquote, .cleanslate dd, .cleanslate fieldset, .cleanslate hr,
/* HTML5 new elements */
.cleanslate article, .cleanslate dialog, .cleanslate figure, .cleanslate footer, .cleanslate header, .cleanslate hgroup, .cleanslate menu, .cleanslate nav, .cleanslate section, .cleanslate audio, .cleanslate video, .cleanslate address, .cleanslate blockquote, .cleanslate colgroup, .cleanslate main, .cleanslate progress, .cleanslate summary {
display:block !important;
}
.cleanslate h1, .cleanslate h2, .cleanslate h3, .cleanslate h4, .cleanslate h5, .cleanslate h6 {
font-weight: bold !important;
}
.cleanslate h1 {
font-size: 2em !important;
padding: .67em 0 !important;
}
.cleanslate h2 {
font-size: 1.5em !important;
padding: .83em 0 !important;
}
.cleanslate h3 {
font-size: 1.17em !important;
padding: .83em 0 !important;
}
.cleanslate h4 {
font-size: 1em !important;
}
.cleanslate h5 {
font-size: .83em !important;
}
.cleanslate p {
margin: 1em 0 !important;
}
.cleanslate table {
display: table !important;
}
.cleanslate thead {
display: table-header-group !important;
}
.cleanslate tbody {
display: table-row-group !important;
}
.cleanslate tfoot {
display: table-footer-group !important;
}
.cleanslate tr {
display: table-row !important;
}
.cleanslate th, .cleanslate td {
display: table-cell !important;
padding: 2px !important;
}
/* == SPECIFIC ELEMENTS == */
/* Some of these are browser defaults; some are just useful resets */
.cleanslate ol, .cleanslate ul {
margin: 1em 0 !important;
}
.cleanslate ul li, .cleanslate ul ul li, .cleanslate ul ul ul li, .cleanslate ol li, .cleanslate ol ol li, .cleanslate ol ol ol li, .cleanslate ul ol ol li, .cleanslate ul ul ol li, .cleanslate ol ul ul li, .cleanslate ol ol ul li {
list-style-position: inside !important;
margin-top: .08em !important;
}
.cleanslate ol ol, .cleanslate ol ol ol, .cleanslate ul ul, .cleanslate ul ul ul, .cleanslate ol ul, .cleanslate ol ul ul, .cleanslate ol ol ul, .cleanslate ul ol, .cleanslate ul ol ol, .cleanslate ul ul ol {
padding-left: 40px !important;
margin: 0 !important;
}
/* helper for general navigation */
.cleanslate nav ul, .cleanslate nav ol {
list-style-type:none !important;
}
.cleanslate ul, .cleanslate menu {
list-style-type:disc !important;
}
.cleanslate ol {
list-style-type:decimal !important;
}
.cleanslate ol ul, .cleanslate ul ul, .cleanslate menu ul, .cleanslate ol menu, .cleanslate ul menu, .cleanslate menu menu {
list-style-type:circle !important;
}
.cleanslate ol ol ul, .cleanslate ol ul ul, .cleanslate ol menu ul, .cleanslate ol ol menu, .cleanslate ol ul menu, .cleanslate ol menu menu, .cleanslate ul ol ul, .cleanslate ul ul ul, .cleanslate ul menu ul, .cleanslate ul ol menu, .cleanslate ul ul menu, .cleanslate ul menu menu, .cleanslate menu ol ul, .cleanslate menu ul ul, .cleanslate menu menu ul, .cleanslate menu ol menu, .cleanslate menu ul menu, .cleanslate menu menu menu {
list-style-type:square !important;
}
.cleanslate li {
display:list-item !important;
/* Fixes IE7 issue with positioning of nested bullets */
min-height:auto !important;
min-width:auto !important;
padding-left: 20px !important; /* replace -webkit-padding-start: 40px; */
}
.cleanslate strong {
font-weight:bold !important;
}
.cleanslate em {
font-style:italic !important;
}
.cleanslate kbd, .cleanslate samp, .cleanslate code, .cleanslate pre {
font-family:monospace !important;
}
.cleanslate a {
color: blue !important;
text-decoration: underline !important;
}
.cleanslate a:visited {
color: #529 !important;
}
.cleanslate a, .cleanslate a *, .cleanslate input[type=submit], .cleanslate input[type=button], .cleanslate input[type=radio], .cleanslate input[type=checkbox], .cleanslate select, .cleanslate button {
cursor:pointer !important;
}
.cleanslate button, .cleanslate input[type=submit] {
text-align: center !important;
padding: 2px 6px 3px !important;
border-radius: 4px !important;
text-decoration: none !important;
font-family: arial, helvetica, sans-serif !important;
font-size: small !important;
background: white !important;
-webkit-appearance: push-button !important;
color: buttontext !important;
border: 1px #a6a6a6 solid !important;
background: lightgrey !important; /* Old browsers */
background: rgb(255,255,255); /* Old browsers */
background: -moz-linear-gradient(top, rgba(255,255,255,1) 0%, rgba(221,221,221,1) 100%, rgba(209,209,209,1) 100%, rgba(221,221,221,1) 100%) !important; /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(255,255,255,1)), color-stop(100%,rgba(221,221,221,1)), color-stop(100%,rgba(209,209,209,1)), color-stop(100%,rgba(221,221,221,1))) !important; /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, rgba(255,255,255,1) 0%,rgba(221,221,221,1) 100%,rgba(209,209,209,1) 100%,rgba(221,221,221,1) 100%) !important; /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, rgba(255,255,255,1) 0%,rgba(221,221,221,1) 100%,rgba(209,209,209,1) 100%,rgba(221,221,221,1) 100%) !important; /* Opera 11.10+ */
background: -ms-linear-gradient(top, rgba(255,255,255,1) 0%,rgba(221,221,221,1) 100%,rgba(209,209,209,1) 100%,rgba(221,221,221,1) 100%) !important; /* IE10+ */
background: linear-gradient(to bottom, rgba(255,255,255,1) 0%,rgba(221,221,221,1) 100%,rgba(209,209,209,1) 100%,rgba(221,221,221,1) 100%) !important; /* W3C */
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#dddddd',GradientType=0 ) !important; /* IE6-9 */
-webkit-box-shadow: 1px 1px 0px #eee !important;
-moz-box-shadow: 1px 1px 0px #eee !important;
-o-box-shadow: 1px 1px 0px #eee !important;
box-shadow: 1px 1px 0px #eee !important;
outline: initial !important;
}
.cleanslate button:active, .cleanslate input[type=submit]:active, .cleanslate input[type=button]:active, .cleanslate button:active {
background: rgb(59,103,158) !important; /* Old browsers */
background: -moz-linear-gradient(top, rgba(59,103,158,1) 0%, rgba(43,136,217,1) 50%, rgba(32,124,202,1) 51%, rgba(125,185,232,1) 100%) !important; /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(59,103,158,1)), color-stop(50%,rgba(43,136,217,1)), color-stop(51%,rgba(32,124,202,1)), color-stop(100%,rgba(125,185,232,1))) !important; /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, rgba(59,103,158,1) 0%,rgba(43,136,217,1) 50%,rgba(32,124,202,1) 51%,rgba(125,185,232,1) 100%) !important; /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, rgba(59,103,158,1) 0%,rgba(43,136,217,1) 50%,rgba(32,124,202,1) 51%,rgba(125,185,232,1) 100%) !important; /* Opera 11.10+ */
background: -ms-linear-gradient(top, rgba(59,103,158,1) 0%,rgba(43,136,217,1) 50%,rgba(32,124,202,1) 51%,rgba(125,185,232,1) 100%) !important; /* IE10+ */
background: linear-gradient(to bottom, rgba(59,103,158,1) 0%,rgba(43,136,217,1) 50%,rgba(32,124,202,1) 51%,rgba(125,185,232,1) 100%) !important; /* W3C */
border-color: #5259b0 !important;
}
.cleanslate button {
padding: 1px 6px 2px 6px !important;
margin-right: 5px !important;
}
.cleanslate input[type=hidden] {
display:none !important;
}
/* restore form defaults */
.cleanslate textarea {
-webkit-appearance: textarea !important;
background: white !important;
padding: 2px !important;
margin-left: 4px !important;
word-wrap: break-word !important;
white-space: pre-wrap !important;
font-size: 11px !important;
font-family: arial, helvetica, sans-serif !important;
line-height: 13px !important;
resize: both !important;
}
.cleanslate select, .cleanslate textarea, .cleanslate input {
border:1px solid #ccc !important;
}
.cleanslate select {
font-size: 11px !important;
font-family: helvetica, arial, sans-serif !important;
display: inline-block;
}
.cleanslate textarea:focus, .cleanslate input:focus {
outline: auto 5px -webkit-focus-ring-color !important;
outline: initial !important;
}
.cleanslate input[type=text] {
background: white !important;
padding: 1px !important;
font-family: initial !important;
font-size: small !important;
}
.cleanslate input[type=checkbox], .cleanslate input[type=radio] {
border: 1px #2b2b2b solid !important;
border-radius: 4px !important;
}
.cleanslate input[type=checkbox], .cleanslate input[type=radio] {
outline: initial !important;
}
.cleanslate input[type=radio] {
margin: 2px 2px 3px 2px !important;
}
.cleanslate abbr[title], .cleanslate acronym[title], .cleanslate dfn[title] {
cursor:help !important;
border-bottom-width:1px !important;
border-bottom-style:dotted !important;
}
.cleanslate ins {
background-color:#ff9 !important;
color:black !important;
}
.cleanslate del {
text-decoration: line-through !important;
}
.cleanslate blockquote, .cleanslate q {
quotes:none !important; /* HTML5 */
}
.cleanslate blockquote:before, .cleanslate blockquote:after, .cleanslate q:before, .cleanslate q:after, .cleanslate li:before, .cleanslate li:after {
content:"" !important;
}
.cleanslate input, .cleanslate select {
vertical-align:middle !important;
}
.cleanslate table {
border-collapse:collapse !important;
border-spacing:0 !important;
}
.cleanslate hr {
display:block !important;
height:1px !important;
border:0 !important;
border-top:1px solid #ccc !important;
margin:1em 0 !important;
}
.cleanslate *[dir=rtl] {
direction: rtl !important;
}
.cleanslate mark {
background-color:#ff9 !important;
color:black !important;
font-style:italic !important;
font-weight:bold !important;
}
.cleanslate menu {
padding-left: 40px !important;
padding-top: 8px !important;
}
/* additional helpers */
.cleanslate [hidden],
.cleanslate template {
display: none !important;
}
.cleanslate abbr[title] {
border-bottom: 1px dotted !important;
}
.cleanslate sub, .cleanslate sup {
font-size: 75% !important;
line-height: 0 !important;
position: relative !important;
vertical-align: baseline !important;
}
.cleanslate sup {
top: -0.5em !important;
}
.cleanslate sub {
bottom: -0.25em !important;
}
.cleanslate img {
border: 0 !important;
}
.cleanslate figure {
margin: 0 !important;
}
.cleanslate textarea {
overflow: auto !important;
vertical-align: top !important;
}
/* == ROOT CONTAINER ELEMENT == */
/* This contains default values for child elements to inherit */
.cleanslate {
font-size: medium !important;
line-height: 1 !important;
direction:ltr !important;
text-align: left !important; /* for IE, Opera */
text-align: start !important; /* recommended W3C Spec */
font-family: "Times New Roman", Times, serif !important; /* Override this with whatever font-family is required */
color: black !important;
font-style:normal !important;
font-weight:normal !important;
text-decoration:none !important;
list-style-type:disc !important;
}
.cleanslate pre {
white-space:pre !important;
}

View File

@ -1,10 +1,13 @@
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const increaseSpecificity = require('postcss-increase-specificity');
const devMode = process.env.NODE_ENV !== 'production';
const defaultConfig = {
mode: 'production',
plugins: [
new CleanWebpackPlugin(['dist/']),
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
@ -20,11 +23,25 @@ const defaultConfig = {
use: ['babel-loader'],
},
{
test: /\.scss$/,
test: /\.(scss|css)$/,
use: [
// fallback to style-loader in development
devMode ? 'style-loader' : MiniCssExtractPlugin.loader,
'css-loader',
'cssimportant-loader',
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: [
increaseSpecificity({
stackableRoot: '.cleanslate',
repeat: 1,
}),
],
sourceMap: devMode,
},
},
'sass-loader',
],
},