3.2.0 • Published 3 years ago

@ibnlanre/recss v3.2.0

Weekly downloads
-
License
MIT
Repository
-
Last release
3 years ago

ReCSS

TypeScript code style: prettier version Twitter


ReCSS is a library that facilitates writing atomic classes for UI elements. From factory, ReCSS comes bundled with a pre-built tailwindcss grammar, yet, supports other CSS frameworks (tachyons, bootstrap, funcssion, basscss, skeleton, quantum etc.) including custom ones; it does this by filtering the atomic styles included within provided stylesheets. As of v3.1.0, we recommend using Atomize to perform that function.


Installation

npm i @ibnlanre/recss

API

Browser

Due to ease of bundling, ReCSS imports light and doesn't utilize code that isn't browser compatible.

<script src="https://unpkg.com/@ibnlanre/recss/index.js"></script>

Import

ReCSS has only one export and it is the default. There should be no need to append .default when using the CommonJS syntax, but it there's an error being thrown, use const { default as ReCSS } = require("@ibnlanre/recss");. With TypeScript's esModuleInterop set to true in tsconfig.json, you can also blend the two syntaxes together.

// NodeJS Require
const ReCSS = require("@ibnlanre/recss");
// ES6 Import
import ReCSS from "@ibnlanre/recss";
// With esModuleInterop
import ReCSS = require("@ibnlanre/recss");

Comments

//→ what the following returns
//? if the precedent is optional
//# denotes the default value
//: means refers | is equivalent to
//! note | warning sign

Overview

This illustration is not entirely necessary as ReCSS is well-typed

var recss = new ReCSS( theme?: (object | string)[] | {}, options? );
var { re, css } = new ReCSS( theme?, options?: {} );

Getters and Setters

These are the options that ReCSS gives you to define the resulting output. Note that they can also be temporarily set using re({ options? }). By temporary, re is expected to be used with css; that way, the options set can be used immediately. But it is not a requirement, since the options object is reset just before css returns.

recss.options = {
computable: boolean; //# true
dissolve: boolean; //# true
filter: string | boolean; //# false
grammar: object //# tailwindcss
matcher: regex //# undefined
objectMode: boolean //# false
orderClass: boolean //# true
orderProps: boolean //# false
reverseOrder: boolean //# false
silent: boolean //# true
}

The theme serves as a getter and setter for themes assigned at initialization. This allows for it for be re-assigned or extended. To re-assign, use an Object i.e. { themes? } and to extend, use an Array i.e. [ themes ]; it is called extension because it works a little bit different from Object.assign() — it extends, not overrides. So, to override existing values, use { ...recss.theme, new_themes } instead.

recss.theme: { } :→ Object.assign() | [] → for extension

Getters Only

The store is a object map of id values to their evaluated results. The id values only get added to the store if they were passed in when calling css(). This is important to note because it serves as a reference for the object returned, which serves as a reference for future amendments later on in the code.

recss.store: this keeps track of the reference store

grammar on the other hand, is an object that returns TailwindCSS class-attribute pairs, else the object assigned to options.grammar. Note that it cannot be re-assigned directly, and contains a mapping that is relatively lengthy printing to stdout. Use util.inspect to assess.

recss.grammar: built from the stylesheet provided

Grammar

// import tailwind from "./tailwind.js"; | use @ibnlanre/atomize

recss.options = {
  // re-assignments override the initial | default values set
  "grammar": atomize({ //?  it is a static method
    file: "../tests/tailwind.css", // the relative css file path
    sep: ":",  //? the separator in atomic classes e.g. sm:px-1
    path: __dirname, //? for easier resolution
    save: false //? if the file should be saved to the system or not
    dest: "./" //? location to save the resulting file to, if { save: true }
  }), //! it returns the grammar object //: "grammar": tailwind,
  "matcher": /[\w-:/]+/, //? for matching atomic class in stylesheet
};

Initialization

ReCSS introduces a new syntax for writing objects, based on the CSS Object Model. It is not an implementation, rather a relatively new way to write JS objects as strins called Read-once object, named after the Read-once function. Note that ReCSS still supports writing objects the JS way.

{ margin: { bottom: "mb-3", left: "ml-2", } } //: becomes "margin.bottom: mb-3; margin.left: ml-2"

When initializing, new ReCSS() can take either an Array or an Object, that would determine the theme. As an array, the values are blended into one — no overrides. To override, use the spread operator i.e. { ...theme1, ...theme2 }. It exposes two instance methods re and css. Use with caution! :)

const recss = new ReCSS([
  "margin.bottom: mb-3; margin.left: ml-2",
  "colors.pink: bg-pink-500; colors.purple: bg-purple-500",
  "margin.top: mt-3; margin.right: mr-4;"
  { button: "px-2 py-3" },
]);

const { re, css } = recss;

Usage

All inputs to css are checked in the theme for existence. It takes strings (as a collection of classes or keys) and objects (either in JSX format or as valid CSS attributes) i.e. marginLeft === "margin-left". When an id is introduced, it serves as a datum for the rest of the input; that is, it is evaluated first. And other values can override it. Note that, although css() doesn't understand Read-once Objects, it does introduce its own syntax for targeting values: strings with "." represent nested values stored in the theme.

//→ { id: 'pink_btn', className: 'px-4 py-4 bg-pink-500 ml-2' }
const pinkButton = css(
  "button px-4",
  { marginLeft: "margin.left" },
  { id: "pink_btn", paddingY: "py-4" },
  "colors.pink"
);

ReCSS filters in two ways, as a boolean and as a string. As a boolean, set to true, ReCSS filters out properties that are not ".className", returning an object with { className?: string }. Whereas, when options.filter is set to a string, it returns a value with a key equal to the passed string. So in React, we can have <div className={css("some classes")} ></div>

recss.options = { filter: true };

Although re can be used to temporarily amend recss.options, it can also be used to redact "id" values saved to the store. This works by passing the "id" name as a argument to re, which in turn returns an object { re: this.re, css() }. Note that the css function returned is not the same as recss.css().

//→ { className: 'px-6 py-3 bg-pink-500 ml-2' } // id filtered
const pinkButton2 = re({ filter: true }).re("pink_btn")
  .css`pink_btn button px-6`;
console.log(pinkButton === pinkButton2); // true

React

Although ReCSS is written for React, it doesn't get its name from it. That note is best left to the Creator.

import React, { useState } from "react";
import { render } from "react-dom";

recss.theme = ["gray_btn: border-gray-700"];
const [isActive, setIsActive] = useState(false);

ReCSS comes with some cool features that makes it perfect for React. Some of which are:

  • MYOB: ReCSS minds its own business. Its core functionality is to evaluate atomic classes and that's exactly what it does, but with a perk; it allows other unrelated properties to be passed in, filtering out those that are needed and spitting out the rest without modifying them. Note that in the following code, onClick() can as well be outside css() and still function accurately.

  • Auto-Prop: if a key with the name is passed in at the top-level of any object, its values are instantly blended without the need for destructuring. This behaviour can be disabled with { autoprop? } set to false.

const GrayButton = (props) => (
  <div
    {...css("button gray_btn", {
      backgroundColor: isActive ? "bg-gray-300" : "",
      onClick: () => setIsActive(!isActive),
      props, //! it's best practice to put props last
    })}
  />
);
  • Baseline: ReCSS allows setting base classes which are protected from getting replaced if evaluated alongside other inputs. As with the example below, css() also accept a { rebase? } property, which gets added to the string input and is affected in the evaluation.
//→ <div class="px-5 py-1 border-gray-700 p-3" onclick="..." ></div>
render(<GrayButton paddingY="py-1" base="p-3" rebase="px-5" />, root);
//! { base? } values are added to className blindly, without vetting
  • Error Mode: When css(" ") is called with an empty string or an error occurs, it returns { className: undefined }. To examine the error, use re({ silent: false }). And in case if you're not satisfied with how a result is evaluated so, and you're curious to find out why, use { objectMode: true }.

  • Templating: ReCSS also supports writing styles with template literals ( cssstyle )

//→ <div id="grey_btn" onclick="..."></div>
render(<GrayButton id="grey_btn" {...css``} />, root);
  • Object Dissolution: Objects can either be dissolved into classes or props
const { css } = new ReCSS({
  image: { src: "./path/to/ }
}, { dissolve: true });

//→ <img alt="some-image" class="w-full" src="./path/to/img.png" />
render(<img {...css`image w-full`} />, root);
  • Computed Properties: theme properties can be used methodically to calculate the resulting class. To reference a method within another, use this.[method_name]. ReCSS introduces another manner in which the function can be called from a string function_name[arg1, arg2] - the comma is not necessary. The reason why it is a square bracket, rather than parentheses is because classes with square brackets are not atomic. And also because, Atomic CSS classes use parentheses.
const { css } = new ReCSS({
    use: (...values, x) => values.map((e) => x + e).join(" "),
    sm: (...val) => this.use(...val, "sm:")
    lg: (...values) => values.map((e) => "lg:" + e).join(" "),
  }, { computable: true });

//→ <div class="px-4 pt-4 pb-4 sm:px-6 sm:pb-6 lg:px-4 lg:pb-4 ></div>
render(<div {...css("px-4 pt-4 pb-4", "sm[px-6 pb-6] lg[px-4, pb-4]")} />, root);
3.2.0

3 years ago

3.1.7

3 years ago

3.1.6

3 years ago

3.1.8

3 years ago

3.1.3

3 years ago

3.1.5

3 years ago

3.1.4

3 years ago

3.1.2

3 years ago

3.1.1

3 years ago

3.1.0

3 years ago

3.0.1

3 years ago

3.0.0

3 years ago

2.1.2

3 years ago

2.1.1

3 years ago

2.1.0

3 years ago

2.0.2

3 years ago

2.0.1

3 years ago

2.0.0

3 years ago

1.0.1

3 years ago

1.0.0

3 years ago