@neolithic-css/toolkit

1.4.0 • Public • Published

Toolkit for generate fully customizable CSS utility classes written in Sass.

version license Sass

Why?

There are tons of CSS frameworks we could choose, while lots of them are just too complex. What I want is just a minimum yet flexible API that could generate all kinds of CSS utilities that match MY requirements easily. Tailwind CSS is cool, but I don't like the name convention of it.

Another thing is that I've never seen a framework that has what I called "Fluid Utility" support. It's really annoying to writing lots of "responsive utility" classes 😣

I wasn't able to find any tools meet my requirements, so I made one.

Installation

You can install it via npm 📦

npm i @neolithic-css/toolkit

Basic Usage

If you use webpack's sass loader, just reference it using the ~ syntax 🤟🏼

Here is a simple example for generating a series of "fluid utilities" for the font-size.

@use "~@neolithic-css/toolkit" as *;

@include add-fluid-utility(
    $property-name: font-size,
    $property-value: 14px 16px 18px 20px,
    $property-name-alias: fz,
    $step: 2px,
);

// Don't forget to call the `build` mixin,
// this mixin outputs all the utilities you added.
@include build;
With the above code, the following CSS will be generated.
.fz\:14 {
    font-size: 14px !important
}

.fz\:16 {
    font-size: 16px !important
}

.fz\:18 {
    font-size: 18px !important
}

.fz\:20 {
    font-size: 20px !important
}

@media (min-width: 320px) {
    .fz\:14\~12 {
        font-size: calc(11.09091px + .28409vw) !important
    }

    .fz\:14\~10 {
        font-size: calc(8.18182px + .56818vw) !important
    }

    .fz\:14\~8 {
        font-size: calc(5.27273px + .85227vw) !important
    }

    .fz\:14\~6 {
        font-size: calc(2.36364px + 1.13636vw) !important
    }

    .fz\:14\~4 {
        font-size: calc(-.54545px + 1.42045vw) !important
    }

    .fz\:14\~2 {
        font-size: calc(-3.45455px + 1.70455vw) !important
    }

    .fz\:14\~0 {
        font-size: calc(1.98864vw - 6.36364px) !important
    }

    .fz\:16\~14 {
        font-size: calc(13.09091px + .28409vw) !important
    }

    .fz\:16\~12 {
        font-size: calc(10.18182px + .56818vw) !important
    }

    .fz\:16\~10 {
        font-size: calc(7.27273px + .85227vw) !important
    }

    .fz\:16\~8 {
        font-size: calc(4.36364px + 1.13636vw) !important
    }

    .fz\:16\~6 {
        font-size: calc(1.45455px + 1.42045vw) !important
    }

    .fz\:16\~4 {
        font-size: calc(-1.45455px + 1.70455vw) !important
    }

    .fz\:16\~2 {
        font-size: calc(-4.36364px + 1.98864vw) !important
    }

    .fz\:16\~0 {
        font-size: calc(2.27273vw - 7.27273px) !important
    }

    .fz\:18\~16 {
        font-size: calc(15.09091px + .28409vw) !important
    }

    .fz\:18\~14 {
        font-size: calc(12.18182px + .56818vw) !important
    }

    .fz\:18\~12 {
        font-size: calc(9.27273px + .85227vw) !important
    }

    .fz\:18\~10 {
        font-size: calc(6.36364px + 1.13636vw) !important
    }

    .fz\:18\~8 {
        font-size: calc(3.45455px + 1.42045vw) !important
    }

    .fz\:18\~6 {
        font-size: calc(.54545px + 1.70455vw) !important
    }

    .fz\:18\~4 {
        font-size: calc(-2.36364px + 1.98864vw) !important
    }

    .fz\:18\~2 {
        font-size: calc(-5.27273px + 2.27273vw) !important
    }

    .fz\:18\~0 {
        font-size: calc(2.55682vw - 8.18182px) !important
    }

    .fz\:20\~18 {
        font-size: calc(17.09091px + .28409vw) !important
    }

    .fz\:20\~16 {
        font-size: calc(14.18182px + .56818vw) !important
    }

    .fz\:20\~14 {
        font-size: calc(11.27273px + .85227vw) !important
    }

    .fz\:20\~12 {
        font-size: calc(8.36364px + 1.13636vw) !important
    }

    .fz\:20\~10 {
        font-size: calc(5.45455px + 1.42045vw) !important
    }

    .fz\:20\~8 {
        font-size: calc(2.54545px + 1.70455vw) !important
    }

    .fz\:20\~6 {
        font-size: calc(-.36364px + 1.98864vw) !important
    }

    .fz\:20\~4 {
        font-size: calc(-3.27273px + 2.27273vw) !important
    }

    .fz\:20\~2 {
        font-size: calc(-6.18182px + 2.55682vw) !important
    }

    .fz\:20\~0 {
        font-size: calc(2.84091vw - 9.09091px) !important
    }
}

@media (min-width: 1024px) {
    .fz\:14\~0, .fz\:14\~2, .fz\:14\~4, .fz\:14\~6, .fz\:14\~8, .fz\:14\~10, .fz\:14\~12 {
        font-size: 14px !important
    }

    .fz\:16\~0, .fz\:16\~2, .fz\:16\~4, .fz\:16\~6, .fz\:16\~8, .fz\:16\~10, .fz\:16\~12, .fz\:16\~14 {
        font-size: 16px !important
    }

    .fz\:18\~0, .fz\:18\~2, .fz\:18\~4, .fz\:18\~6, .fz\:18\~8, .fz\:18\~10, .fz\:18\~12, .fz\:18\~14, .fz\:18\~16 {
        font-size: 18px !important
    }

    .fz\:20\~0, .fz\:20\~2, .fz\:20\~4, .fz\:20\~6, .fz\:20\~8, .fz\:20\~10, .fz\:20\~12, .fz\:20\~14, .fz\:20\~16, .fz\:20\~18 {
        font-size: 20px !important
    }
}

@media (max-width: 320px) {
    .fz\:14\~12 {
        font-size: 12px !important
    }

    .fz\:14\~10 {
        font-size: 10px !important
    }

    .fz\:14\~8 {
        font-size: 8px !important
    }

    .fz\:14\~6 {
        font-size: 6px !important
    }

    .fz\:14\~4 {
        font-size: 4px !important
    }

    .fz\:14\~2 {
        font-size: 2px !important
    }

    .fz\:14\~0 {
        font-size: 0 !important
    }

    .fz\:16\~14 {
        font-size: 14px !important
    }

    .fz\:16\~12 {
        font-size: 12px !important
    }

    .fz\:16\~10 {
        font-size: 10px !important
    }

    .fz\:16\~8 {
        font-size: 8px !important
    }

    .fz\:16\~6 {
        font-size: 6px !important
    }

    .fz\:16\~4 {
        font-size: 4px !important
    }

    .fz\:16\~2 {
        font-size: 2px !important
    }

    .fz\:16\~0 {
        font-size: 0 !important
    }

    .fz\:18\~16 {
        font-size: 16px !important
    }

    .fz\:18\~14 {
        font-size: 14px !important
    }

    .fz\:18\~12 {
        font-size: 12px !important
    }

    .fz\:18\~10 {
        font-size: 10px !important
    }

    .fz\:18\~8 {
        font-size: 8px !important
    }

    .fz\:18\~6 {
        font-size: 6px !important
    }

    .fz\:18\~4 {
        font-size: 4px !important
    }

    .fz\:18\~2 {
        font-size: 2px !important
    }

    .fz\:18\~0 {
        font-size: 0 !important
    }

    .fz\:20\~18 {
        font-size: 18px !important
    }

    .fz\:20\~16 {
        font-size: 16px !important
    }

    .fz\:20\~14 {
        font-size: 14px !important
    }

    .fz\:20\~12 {
        font-size: 12px !important
    }

    .fz\:20\~10 {
        font-size: 10px !important
    }

    .fz\:20\~8 {
        font-size: 8px !important
    }

    .fz\:20\~6 {
        font-size: 6px !important
    }

    .fz\:20\~4 {
        font-size: 4px !important
    }

    .fz\:20\~2 {
        font-size: 2px !important
    }

    .fz\:20\~0 {
        font-size: 0 !important
    }
}

The above example generates a bunch of "fluid utilities". For example the class .fz:20~14 indicates font-size gradually changes from 20px to 14px when viewport get resized from 1024px to 320px(by default).

Sure you could generate the good old "responsive utilities" as well by utilizing the add-simple-utility or add-compound-utility mixin (I'll explain the difference between them later on).

For instance

@include add-simple-utility(
    $property-name: font-size,
    $property-value: 14px 16px 18px 20px,
    $property-name-alias: fz,
);
That will give you this generated CSS
.fz\:14px {
    font-size: 14px !important
}

.fz\:16px {
    font-size: 16px !important
}

.fz\:18px {
    font-size: 18px !important
}

.fz\:20px {
    font-size: 20px !important
}

@media (max-width: 1600px) {
    .fz\:14px\@xxl {
        font-size: 14px !important
    }

    .fz\:16px\@xxl {
        font-size: 16px !important
    }

    .fz\:18px\@xxl {
        font-size: 18px !important
    }

    .fz\:20px\@xxl {
        font-size: 20px !important
    }
}

@media (max-width: 1256px) {
    .fz\:14px\@xl {
        font-size: 14px !important
    }

    .fz\:16px\@xl {
        font-size: 16px !important
    }

    .fz\:18px\@xl {
        font-size: 18px !important
    }

    .fz\:20px\@xl {
        font-size: 20px !important
    }
}

@media (max-width: 1024px) {
    .fz\:14px\@lg {
        font-size: 14px !important
    }

    .fz\:16px\@lg {
        font-size: 16px !important
    }

    .fz\:18px\@lg {
        font-size: 18px !important
    }

    .fz\:20px\@lg {
        font-size: 20px !important
    }
}

@media (max-width: 768px) {
    .fz\:14px\@md {
        font-size: 14px !important
    }

    .fz\:16px\@md {
        font-size: 16px !important
    }

    .fz\:18px\@md {
        font-size: 18px !important
    }

    .fz\:20px\@md {
        font-size: 20px !important
    }
}

@media (max-width: 550px) {
    .fz\:14px\@sm {
        font-size: 14px !important
    }

    .fz\:16px\@sm {
        font-size: 16px !important
    }

    .fz\:18px\@sm {
        font-size: 18px !important
    }

    .fz\:20px\@sm {
        font-size: 20px !important
    }
}

@media (max-width: 320px) {
    .fz\:14px\@xs {
        font-size: 14px !important
    }

    .fz\:16px\@xs {
        font-size: 16px !important
    }

    .fz\:18px\@xs {
        font-size: 18px !important
    }

    .fz\:20px\@xs {
        font-size: 20px !important
    }
}

For example the class .fz:20px@xl indicates font-size: 20px applied at viewport size xl(1256px). Well the naming convention is totally up to you. You could config the settings by initializing the global configurations values or specify the settings via the mixin directly.

Global Configurations

Below are the default values of global configurations.

$breakpoints: (
    xs: 320px,
    sm: 550px,
    md: 768px,
    lg: 1024px,
    xl: 1256px,
    xxl: 1600px,
) !default;

// default settings for generating fluid utilities
$fluid: (
    start-shrinking-width: default(map.get($breakpoints, lg), 1024px),
    stop-shrinking-width: default(map.get($breakpoints, xs), 320px),
    strip-unit: true,
    step: 10px,
    min: 0px,
) !default;

$property-name-delimiter: ':' !default;
$query-delimiter: '@' !default;
$important: true;

For example, if you'd like to giving your own breakpoints, you overwrite the default one by utilizing the with keyword.

@use "~@neolithic-css/toolkit" as * with
(
    $breakpoints: (
        sm: 500px,
        md: 900px,
        lg: 1300px,
        xs: 300px,
    ),
    $important: false, // do not append `!important` at the end of css rules.
);

Or you may overwrite it via mixin directly like this.

@include add-simple-utility(
    $property-name: font-size,
    $property-value: 14px 16px 18px 20px,
    $property-name-alias: fz,
    
    // notice that only `$breakpoints` can't be overwrite in mixin,
    // here you are saying only output utility classes for size `sm` and `md`. 
    $breakpoints: (sm, md), 
    $important: false,
);

Naming Convention

By default, the CSS classname is consists of several parts.

  • $property-name-alias
  • $property-value-alias
  • $property-name-delimiter
  • $query-delimiter
  • $breakpoint-name

For example, this is the simplest form you can generate a utility.

@include add-simple-utility(
    $property-name: display,
    $property-value: block,
);

This piece of code gives you :

.display\:block {
    display: block !important;
}

/* utilities with media query */
@media (max-width: 320px) {
    .display\:block\@xs {
        display: block !important;
    }
}

in the classname .display:block@xs, where

  • display$property-name-alias
  • :$property-name-delimiter
  • block$property-value-alias
  • @$query-delimiter
  • xs$breakpoint-name

You can change these parts to whatever you want via global configurations or mixin arguments, or totally ignore them by specifying a $class argument to the mixin.

I'm not trying to impose my opinion to anyone, but I thought it would be clearer to explicitly separate each part by their functions instead of just joining them all together with the -.

As I said, you can pass a $class argument to the mixin if you are not cool with this 🙏🏼

Simple-Utility vs. Compound-Utility

  • Simple-Utility : utilities which have only one CSS rule.
  • Compound-Utility : utilities which have multiple CSS rules.

For example:

.simple-utility {
    margin-top: 10px;
}

.compound-utility {
    margin-top: 10px;
    font-size: 20px;
}

I grouped utilities into these two types because of the naming convention. That is, a compound utility can't have a $property-value-alias and $property-name-alias.

There are three main mixins you can use to generate utilities:

  • add-simple-utility
  • add-fluid-utility
  • add-compound-utility

As the names of the mixins implied, you use different mixin to generate different type of utilities. The "fluid utility" is simple-utility too.

Tips

There are several helper functions provided within this package. Before you can use them, make sure you have imported them via the @use keyword.

@use "~@neolithic-css/toolkit/src/functions" as *;

The range helper function

Say if you want to generate a series of utilities like this:

.grid-cols\:12 {
  grid-template-columns: repeat(12, minmax(0, 1fr)) !important;
}

.grid-cols\:11 {
  grid-template-columns: repeat(11, minmax(0, 1fr)) !important;
}

.grid-cols\:10 {
  grid-template-columns: repeat(10, minmax(0, 1fr)) !important;
}

.grid-cols\:9 {
  grid-template-columns: repeat(9, minmax(0, 1fr)) !important;
}

.grid-cols\:8 {
  grid-template-columns: repeat(8, minmax(0, 1fr)) !important;
}

.grid-cols\:7 {
  grid-template-columns: repeat(7, minmax(0, 1fr)) !important;
}

.grid-cols\:6 {
  grid-template-columns: repeat(6, minmax(0, 1fr)) !important;
}

.grid-cols\:5 {
  grid-template-columns: repeat(5, minmax(0, 1fr)) !important;
}

.grid-cols\:4 {
  grid-template-columns: repeat(4, minmax(0, 1fr)) !important;
}

.grid-cols\:3 {
  grid-template-columns: repeat(3, minmax(0, 1fr)) !important;
}

.grid-cols\:2 {
  grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
}

.grid-cols\:1 {
  grid-template-columns: repeat(1, minmax(0, 1fr)) !important;
}

In such of cases you can utilize the range helper function like this:

@include add-simple-utility(
    $property-name: grid-template-columns,
    $property-value: range($from: 1, $to: 12, $step: 1, $template: 'repeat(_$i_, minmax(0, 1fr))'),
    $property-name-alias: 'grid-cols',
    $property-value-alias: range($from: 1, $to: 12, $step: 1),
);

which is equivalent to:

@include add-simple-utility(
    $property-name: grid-template-columns,
    $property-value: (
        'repeat(12, minmax(0, 1fr))',
        'repeat(11, minmax(0, 1fr))',
        'repeat(10, minmax(0, 1fr))',
        'repeat(9, minmax(0, 1fr))',
        'repeat(8, minmax(0, 1fr))',
        'repeat(7, minmax(0, 1fr))',
        'repeat(6, minmax(0, 1fr))',
        'repeat(5, minmax(0, 1fr))',
        'repeat(4, minmax(0, 1fr))',
        'repeat(3, minmax(0, 1fr))',
        'repeat(2, minmax(0, 1fr))',
        'repeat(1, minmax(0, 1fr))',
    ),
    $property-name-alias: 'grid-cols',
    $property-value-alias: (12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1),
);

add-**-utilities helper mixin

Each of those add-**-utility mixins has a corresponded add-**-utilities helper that can accept a list, so you can register multiple utilities at once.

  • add-simple-utilities
  • add-fluid-utilities
  • add-compound-utilities

For example:

$simple-utilities: (
    (
        property-name: background-color,
        property-value: (#68D391, #F6E05E, #dc3545),
        property-name-alias: 'bg',
        property-value-alias: (green, yellow, red)
    ),
    (
        property-name: display,
        property-value: (grid, block, inline-block),
        property-name-alias: 'd',
    ),
    (
        property-name: grid-template-columns,
        property-value: range(1, 12, 1, 'repeat(_$i_, minmax(0, 1fr))'),
        property-name-alias: 'grid-cols',
        property-value-alias: range(1, 12, 1)
    ),
    (
        property-name: gap,
        property-value: range(0, 2.5rem, 0.25rem),
        property-value-alias: range(0, 10, 1),
        property-name-delimiter: '-',
        query-delimiter: '|',
    ),
);


@include add-simple-utilities($simple-utilities...);

Contribution

I really tried to keep this package be simple and lightweight. If you are missing something, feel free to open a pull request 👋

Package Sidebar

Install

npm i @neolithic-css/toolkit

Weekly Downloads

1

Version

1.4.0

License

MIT

Unpacked Size

55.3 kB

Total Files

15

Last publish

Collaborators

  • yaquawa