@ibnlanre/recss
v3.2.0
Published
ReCSS is a library that facilitates writing atomic classes for UI elements
Downloads
17
Readme
ReCSS
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 outsidecss()
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 tofalse
.
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, usere({ 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 ( css
style
)
//→ <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, usethis.[method_name]
. ReCSS introduces another manner in which the function can be called from a stringfunction_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);