@use 'sass:map'; | |
@use 'variants' as * with ( | |
$breakpoints: ( | |
'small': 640px, | |
'medium': 768px, | |
'large': 1024px, | |
'wide': 1280px, | |
) | |
); | |
// ASCII Art Generator | |
// http://www.patorjk.com/software/taag/#p=display&v=0&f=ANSI%20Shadow&t=variant | |
// ██████╗ █████╗ ███╗ ██╗ █████╗ ███╗ ██╗ █████╗ | |
// ██╔══██╗██╔══██╗████╗ ██║██╔══██╗████╗ ██║██╔══██╗ | |
// ██████╔╝███████║██╔██╗ ██║███████║██╔██╗ ██║███████║ | |
// ██╔══██╗██╔══██║██║╚██╗██║██╔══██║██║╚██╗██║██╔══██║ | |
// ██████╔╝██║ ██║██║ ╚████║██║ ██║██║ ╚████║██║ ██║ | |
// ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝ | |
.banana { | |
@include variants('responsive' 'hover' 'group-hover') { | |
color: #FFE135; | |
} | |
} | |
// ████████╗███████╗██╗ ██╗████████╗ | |
// ╚══██╔══╝██╔════╝╚██╗██╔╝╚══██╔══╝ | |
// ██║ █████╗ ╚███╔╝ ██║ | |
// ██║ ██╔══╝ ██╔██╗ ██║ | |
// ██║ ███████╗██╔╝ ██╗ ██║ | |
// ╚═╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ | |
$text: ( | |
'left': left, | |
'right': right, | |
'center': center, | |
'justify': justify, | |
); | |
.text { | |
@include options($text, 'responsive') using ($value) { | |
text-align: $value; | |
} | |
} | |
// ███████╗██████╗ █████╗ ██████╗██╗███╗ ██╗ ██████╗ | |
// ██╔════╝██╔══██╗██╔══██╗██╔════╝██║████╗ ██║██╔════╝ | |
// ███████╗██████╔╝███████║██║ ██║██╔██╗ ██║██║ ███╗ | |
// ╚════██║██╔═══╝ ██╔══██║██║ ██║██║╚██╗██║██║ ██║ | |
// ███████║██║ ██║ ██║╚██████╗██║██║ ╚████║╚██████╔╝ | |
// ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝╚═╝ ╚═══╝ ╚═════╝ | |
$spacing: ( | |
'px': 1px, | |
'0': 0, | |
'1': .25rem, | |
'2': .5rem, | |
'3': .75rem, | |
'4': 1rem, | |
'5': 1.25rem, | |
'6': 1.5rem, | |
'8': 2rem, | |
'10': 2.5rem, | |
'12': 3rem, | |
'16': 4rem, | |
'20': 5rem, | |
'24': 6rem, | |
'32': 8rem, | |
'40': 10rem, | |
'48': 12rem, | |
'56': 14rem, | |
'64': 16rem, | |
); | |
// Add auto to the positive margin classes | |
$margin: map.merge(( | |
'auto': auto | |
), $spacing); | |
@include variants('responsive') using ($props...) { | |
// ██████╗ █████╗ ██████╗ ██████╗ ██╗███╗ ██╗ ██████╗ | |
// ██╔══██╗██╔══██╗██╔══██╗██╔══██╗██║████╗ ██║██╔════╝ | |
// ██████╔╝███████║██║ ██║██║ ██║██║██╔██╗ ██║██║ ███╗ | |
// ██╔═══╝ ██╔══██║██║ ██║██║ ██║██║██║╚██╗██║██║ ██║ | |
// ██║ ██║ ██║██████╔╝██████╔╝██║██║ ╚████║╚██████╔╝ | |
// ╚═╝ ╚═╝ ╚═╝╚═════╝ ╚═════╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ | |
.p { | |
@include options($spacing, $props...) using ($value) { | |
padding: $value; | |
} | |
} | |
.px { | |
@include options($spacing, $props...) using ($value) { | |
padding-right: $value; | |
padding-left: $value; | |
} | |
} | |
.py { | |
@include options($spacing, $props...) using ($value) { | |
padding-bottom: $value; | |
padding-top: $value; | |
} | |
} | |
.pt { | |
@include options($spacing, $props...) using ($value) { | |
padding-top: $value; | |
} | |
} | |
.pr { | |
@include options($spacing, $props...) using ($value) { | |
padding-right: $value; | |
} | |
} | |
.pb { | |
@include options($spacing, $props...) using ($value) { | |
padding-bottom: $value; | |
} | |
} | |
.pl { | |
@include options($spacing, $props...) using ($value) { | |
padding-left: $value; | |
} | |
} | |
// ███╗ ███╗ █████╗ ██████╗ ██████╗ ██╗███╗ ██╗ | |
// ████╗ ████║██╔══██╗██╔══██╗██╔════╝ ██║████╗ ██║ | |
// ██╔████╔██║███████║██████╔╝██║ ███╗██║██╔██╗ ██║ | |
// ██║╚██╔╝██║██╔══██║██╔══██╗██║ ██║██║██║╚██╗██║ | |
// ██║ ╚═╝ ██║██║ ██║██║ ██║╚██████╔╝██║██║ ╚████║ | |
// ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝╚═╝ ╚═══╝ | |
.m { | |
@include options($margin, $props...) using ($value) { | |
margin: $value; | |
} | |
} | |
.mx { | |
@include options($margin, $props...) using ($value) { | |
margin-right: $value; | |
margin-left: $value; | |
} | |
} | |
.my { | |
@include options($margin, $props...) using ($value) { | |
margin-bottom: $value; | |
margin-top: $value; | |
} | |
} | |
.mt { | |
@include options($margin, $props...) using ($value) { | |
margin-top: $value; | |
} | |
} | |
.mr { | |
@include options($margin, $props...) using ($value) { | |
margin-right: $value; | |
} | |
} | |
.mb { | |
@include options($margin, $props...) using ($value) { | |
margin-bottom: $value; | |
} | |
} | |
.ml { | |
@include options($margin, $props...) using ($value) { | |
margin-left: $value; | |
} | |
} | |
// ███╗ ███╗ █████╗ ██████╗ ██████╗ ██╗███╗ ██╗ | |
// ████╗ ████║██╔══██╗██╔══██╗██╔════╝ ██║████╗ ██║ | |
// █████╗██╔████╔██║███████║██████╔╝██║ ███╗██║██╔██╗ ██║ | |
// ╚════╝██║╚██╔╝██║██╔══██║██╔══██╗██║ ██║██║██║╚██╗██║ | |
// ██║ ╚═╝ ██║██║ ██║██║ ██║╚██████╔╝██║██║ ╚████║ | |
// ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝╚═╝ ╚═══╝ | |
.-m { | |
@include options($spacing, $props...) using ($value) { | |
margin: -$value; | |
} | |
} | |
.-mx { | |
@include options($spacing, $props...) using ($value) { | |
margin-right: -$value; | |
margin-left: -$value; | |
} | |
} | |
.-my { | |
@include options($spacing, $props...) using ($value) { | |
margin-bottom: -$value; | |
margin-top: -$value; | |
} | |
} | |
.-mt { | |
@include options($spacing, $props...) using ($value) { | |
margin-top: -$value; | |
} | |
} | |
.-mr { | |
@include options($spacing, $props...) using ($value) { | |
margin-right: -$value; | |
} | |
} | |
.-mb { | |
@include options($spacing, $props...) using ($value) { | |
margin-bottom: -$value; | |
} | |
} | |
.-ml { | |
@include options($spacing, $props...) using ($value) { | |
margin-left: -$value; | |
} | |
} | |
} | |
// ██╗ █████╗ ██╗ ██╗ ██████╗ ██╗ ██╗████████╗ | |
// ██║ ██╔══██╗╚██╗ ██╔╝██╔═══██╗██║ ██║╚══██╔══╝ | |
// ██║ ███████║ ╚████╔╝ ██║ ██║██║ ██║ ██║ | |
// ██║ ██╔══██║ ╚██╔╝ ██║ ██║██║ ██║ ██║ | |
// ███████╗██║ ██║ ██║ ╚██████╔╝╚██████╔╝ ██║ | |
// ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ | |
$display: ( | |
'block': block, | |
'inline-block': inline-block, | |
'inline': inline, | |
'flex': flex, | |
'inline-flex': inline-flex, | |
'grid': grid, | |
'inline-grid': inline-grid, | |
'hidden': none, | |
) !default; | |
@include options($display, 'responsive') using ($value) { | |
display: $value; | |
} |
@use 'sass:list' | |
@use 'sass:meta' | |
@use 'sass:selector' | |
@use 'sass:string' | |
/// List of available breakpoints. | |
/// | |
/// @type Map | |
$breakpoints: () !default | |
/// The position of the breakpoint to the selector. | |
/// | |
/// @type String | |
$breakpoint-position: 'before' !default | |
/// The string used to attach breakpoints. | |
/// | |
/// @type String | |
$breakpoint-glue: '\\:' !default | |
/// The position of the variant to the selector. | |
/// | |
/// @type String | |
$variant-position: 'before' !default | |
/// The string used to attach variants. | |
/// | |
/// @type String | |
$variant-glue: '\\:' !default | |
/// The string used to attach options. | |
/// | |
/// @type String | |
$option-glue: '-' !default | |
/// Unset a given value from a list. | |
/// | |
/// @param {List} $list - The string that will be trimmed. | |
/// @param {String} $value - Value that you want to be removed. | |
/// @return {List} Returns the filtered list. | |
@function _remove($list, $value) | |
$result: () | |
@for $i from 1 through list.length($list) | |
@if list.nth($list, $i) != $value | |
$result: list.append($result, list.nth($list, $i)) | |
@return $result | |
/// Generates the selector depending on the current variant and breakpoint. | |
/// | |
/// @param {String} $variant [null] | |
/// @param {String} $breakpoint [null] | |
/// @return {List} Returns the updated selector. | |
@function _selector($variant: null, $breakpoint: null) | |
$selectors: () | |
@each $selector in & | |
$selector: list.nth($selector, 1) | |
@if meta.type-of($selector) == 'string' | |
$selector: string.slice($selector, 2) | |
// Add the variant to the current selector | |
@if $variant | |
@if $variant-position == 'after' | |
$selector: '#{$selector}#{$variant-glue}#{$variant}' | |
@else | |
$selector: '#{$variant}#{$variant-glue}#{$selector}' | |
// Add the breakpoint to the current selector | |
@if $breakpoint | |
@if $breakpoint-position == 'after' | |
$selector: '#{$selector}#{$breakpoint-glue}#{$breakpoint}' | |
@else | |
$selector: '#{$breakpoint}#{$breakpoint-glue}#{$selector}' | |
// Convert back to a class | |
@if $selector | |
$selectors: list.append($selectors, '.#{$selector}', comma) | |
@return $selectors | |
/// Appends a pseudo-class to a selector. | |
/// | |
/// @param {String} $selector | |
/// @param {String} $pseudo-class [null] | |
/// @return {String} Returns the updated selector. | |
@function _pseudo-class($selector, $pseudo-class: null) | |
@if $pseudo-class | |
$selector: selector.nest($selector, '&:#{$pseudo-class}') | |
@return $selector | |
/// Generates the css for a variant and breakpoint. | |
/// | |
/// @param {String} $variant | |
/// @param {String} $breakpoint | |
@mixin _variant($variant, $breakpoint) | |
$pseudo-class: $variant | |
$group: null | |
// Handle group variants | |
@if meta.type-of($variant) == 'string' and string.index($variant, 'group-') | |
$pseudo-class: string.slice($pseudo-class, 7) | |
$group: '.group' | |
// Get the selector depending on the current variant and breakpoint | |
$selector: _selector($variant, $breakpoint) | |
@if $group | |
#{_pseudo-class($group, $pseudo-class)} | |
#{$selector} | |
@content | |
@else | |
#{_pseudo-class($selector, $pseudo-class)} | |
@content | |
/// Distributes the input to the correct output. | |
/// | |
/// @param {List} $variants | |
/// @param {String} $breakpoint | |
@mixin _io($variants, $breakpoint) | |
@if & | |
@include _variant(null, $breakpoint) | |
@content | |
@each $variant in $variants | |
@include _variant($variant, $breakpoint) | |
@content | |
@else | |
@content($variants, $breakpoint) | |
/// Generates the css for the given variants. | |
/// | |
/// @param {List} $variants [()] | |
/// @param {String} $breakpoint [null] | |
@mixin variants($variants: (), $breakpoint: null) | |
// Check for the responsive variant | |
$responsive: list.index($variants, 'responsive') | |
@if $responsive | |
$variants: _remove($variants, 'responsive') | |
@at-root | |
@include _io($variants, $breakpoint) using ($data...) | |
@content($data...) | |
@if $responsive | |
@each $breakpoint, $value in $breakpoints | |
@media screen and (min-width: #{$value}) | |
@include _io($variants, $breakpoint) using ($data...) | |
@content($data...) | |
/// Responsive alias. | |
/// | |
/// @see {Mixin} variants | |
/// @param {List} $variants [()] | |
@mixin responsive($variants: ()) | |
// Make responsive by default | |
$variants: list.append($variants, 'responsive', space) | |
@include variants($variants) using ($data...) | |
@content($data...) | |
/// Helper for creating option based modifiers or classes. | |
/// | |
/// @param {Map} $options | |
/// @param {List} $variants [()] | |
/// @param {List} $breakpoint [null] | |
@mixin options($options, $variants: (), $breakpoint: null) | |
@if & | |
// Render options as a class modifier | |
@each $key, $value in $options | |
&#{$option-glue}#{$key} | |
@include variants($variants, $breakpoint) | |
@content($value) | |
@else | |
// Render options as seperate classes | |
@include variants($variants, $breakpoint) using ($props...) | |
@each $key, $value in $options | |
.#{$key} | |
@include variants($props...) | |
@content($value) |
@devhoussam Your desired syntax is not possible.
You should be able to use it like this though @include variants(('responsive', 'hover', 'group-hover')) {}
@cssninjaStudio what version of sass are you using?
@lukaskleinschmidt using gulp-sass 4.1.0. VS Code doesn't validate the file as well.
I think gulp-sass
uses node-sass
out of the box. As far as I know LibSass (node-sass
) still has not catched up to Dart Sass yet.
You need to change the sass compiler in your gulp setup to use the Dart Sass (sass
) implementation. This should fix the issue.
var gulp = require('gulp');
var sass = require('gulp-sass');
sass.compiler = require('sass');
@lukaskleinschmidt thanks for the answer, I tried that but it does not work. I have the latest gulp-sass and here is what I have in the gulpfile
const { src, dest, task, watch, series, parallel } = require('gulp');
const del = require('del');
const options = require("./config");
const browserSync = require('browser-sync').create();
const sass = require('gulp-sass');
const bourbon = require('node-bourbon').includePaths;
const postcss = require('gulp-postcss');
const concat = require('gulp-concat');
const uglify = require('gulp-uglify');
const imagemin = require('gulp-imagemin');
const cleanCSS = require('gulp-clean-css');
const purgecss = require('gulp-purgecss');
const sourcemaps = require('gulp-sourcemaps');
const autoprefixer = require('gulp-autoprefixer');
const panini = require('panini');
const browserify = require("browserify");
const babelify = require("babelify");
const source = require("vinyl-source-stream");
const nodepath = 'node_modules/';
sass.compiler = require('sass');
When trying to run gulp I get this:
Error: Cannot find module 'sass'
Should I install a package that I don't have? I read dart-sass is bundled with gulp-sass so that should work, shouldn't it?
From the error it sems like it is not installed. So I would try to install it manually npm i -D sass
@lukas Kleinschmidt Got it working, thanks !
@lukaskleinschmidt Thank you!
Can anyone convert the variants.sass
to scss
?
https://jsonformatter.org/sass-to-scss doesn't do the trick
Is the license on this MIT / ISC? Any of it derived from elsewhere?
Nice work @lukaskleinschmidt! I really like Tailwind but that JS when SASS could handle it easily and does not make you depend on a complex toolchain + rules such as @extend .text-right
will work perfectly.
Is there a SASS library which cover almost every Tailwind stuff out there? Because may be your work can be a great start!
Actually it was written in SCSS
at the beginning but I switched to Sass
because it was a bit easier to read.
I also have a updated version which in the end is now written in SCSS
again because working with lists or maps in Sass
was just horrible.
In addition I created a repo to give this thing a proper place to live (and MIT License) as it seems that there are actually people using this
https://github.com/lukaskleinschmidt/snug
It would be super interesting to see how you use this. Or how you would like to use it. Feel free to leave a comment here:
lukaskleinschmidt/snug#1
I created some predefined helpers some time ago. You can have a look at it here and how you can use them here.
Thanks for making this great tool--I have found it really useful so far when spinning up new projects. Currently, I am attempting to create classes that all share the same parent :root.night
but am struggling to figure out how to do so with the options mixin.
I have been able to create exactly what I want in terms of utility classes like this ->
.night\:bg { @include options($colors) using ($value) { background: $value; } }
But cant quite figure out how to add a parent selector so that I yield css results like this:
:root.night .night\:bg-blue-100 { background: #f3f4f6; }
and so on..
Have been stumped on this for awhile so thank you in advance
@nrsimonelli This is not possible with the current script in this gist.
But since this seems to be a feature that might be useful for others as well, I've integrated it in the updated version
https://github.com/lukaskleinschmidt/snug
$colors: (
'blue': (
100: #e5f1fd,
200: #b0d6f9,
300: #7bbaf6,
400: #469ff2,
500: #1183ee,
600: #0d66b9,
700: #094984,
800: #062c4f,
900: #020f1a,
)
);
:root.night {
.night\:bg {
@include options($colors) using ($value) {
background-color: $value;
}
}
}
That is excellent! Thank you for doing this :)
@lukaskleinschmidt The variants.sass throws an error in my compiler. It seems it is not valid: