Cast your styles like a (good) wizard
Long story short:
This is another JSS library for React.
Unlike (most) others, it doesn't rely on context.
Instead, it just relies on normal class names.
It is designed to be compatible with Material Design and MUI (v4 / v5 / v6).
And so that we can more easily extract the generated styles into an external stylesheet.
You can easily add Themes and global styles (and spells), and customize existing ones.
Eventually, you can move the styles dependency to live in one of your company's core react libs.
And remove all magic from your application code!
npm i --save-dev @tty-pt/styles # if you are developing a library add it also as a peer dependency.
import React from "react";
import "@tty-pt/styles"
export default
function App() {
// the div will have a padding of 16px
// and horizontal (and vertical) separatiions of 16px
return (<>
<div className="horizontal pad">
<span>1</span>
<span>2</span>
<span>3</span>
</div>
</>):
}
import React from "react";
import { createTheme, registerGetTheme, makeMagic, withMagic } from "@tty-pt/styles";
const colorTable = {
["StatusSuccess", "#00ff00"],
};
const theme = createTheme({
palette: {
color: [{
func: x => colorTable[x],
length: colorTable.length,
}],
}
});
registerGetTheme(() => theme);
makeMagic({
horizontal: {
gap: "12px !important", // this will override original
},
});
export default withMagic(function App() {
const [themeName, setThemeName] = useState("light");
function toggle() {
setTheme(themeName === "light" ? "dark" : "light");
}
return (<div className={themeName + " horizontal"}>
<span>hello<span>
<span className="color-status-success">world<span>
<button onClick={toggle}>test</button>
</div>):
});
This library is designed to be compatible with material-ui. There are other lower level APIs as well. We will attempt to generate more extensive documentation in the future.
Axis can be horizontal or vertical. In some situations it can be omitted (meaning all axis).
Size can be the default (unit) size, which is usually when the label is omitted. It can also be small (half) or big (double), smallest (a fourth) or biggest (quadruple). Size can also be 0. When you apply "neg" after the size label, it negates the value. When used with a unit, usually the value is on the left, and the unit is on the right.
Position can be relative or absolute
There is also a sort of position for setting the element to the top, to the right, etc. This will be discussed later.
Direction can be top, left, right or bottom.
Align can be stretch, center, end (meaning flex-end) or start (meaning flex-start). If you hide this label, it usually means stretch.
This label might mean "space-around", "space-between", "center", "start" (meaning flex-start) or "end" (meaning flex-end). If you hide it it means "center".
This can be "left", "right" or "center". Defaults to "center".
This can be "resize", "horizontal-<horizontal-cursor>" or "vertical-<vertical-cursor>".
Defaults to "ew-resize".
Defaults to "ns-resize".
Color can be inherit, which is the default. it can also be black or white. Color kind of depends on the context (well 2 colors). If you omit the color label, and you are specifying a text color, you get the primary text color. If you do the same while specifying a background color, you get the paper color. Only in that first case (text color), might you specify "secondary". While only in that second case (background color) you can use the "body" label. Then, you have success, warning error and info. For each of those you have main, light and dark. You can also use colors specified via color octaves (see Theming section).
Defaults to "fixed".
defaults to "bold" for now, meaning 600.
Defaults to "pi-over-two".
Spells have two sorts of names or shapes:
- One is camelcased, in case you wish to do things that way.
- The other is with-dashes-like-this. This is prefered.
Here I will present mostly the dash-like syntax.
Spells can be inspired on CSS property keys and values, although this is not the case all the time. Beware that this is based on a promise, and some of these spells might not work in certain combinations yet (this library is still a work in progress).
Display this element with its children along <axis>, separated by <size>.
The same as 0 but does not use flex
The same as 0 but for tables
Add padding to this element in <axis> direction, of size <size>. Neg can only be used along with with direction.
This sets the element (or the element's children) to have a flex-grow property of 1.
Use this in conjunction with 0.
Sets the position of the element to the label (meaning relative or absolute).
Place the element in the specified <direction>, far from the edge by <size>. For use with absolute positioning and the like.
This sets the element's property of align-self to the value of <align>.
This sets the element's property of align-items to the value of <align>. In this case <align> defaults to center.
This sets the element's property of justifyContent to the value of <justify>.
This sets the element's (default or maximum) size to be <size> in the specified axis. In this specific case, <size> can also be "full" meaning "100%".
This sets the element's textAlign property to the value of <text-align>.
I'm sure you can guess it at this point. I don't recomend using margins very often.
Now the labels have the most important information.
Sets the color property of the element.
Sets the background-color property of the element.
Sets the border in the specified direction or axis to be of the specified color.
Set the element's table-layout property to the specified <table-layout>.
Set the element's font-weight property to the specified <font-weight>.
Rotate this element by <angle>
Apply <overflow> as value of overflow property.
Like in MUI, we have a Theme type of object. It is very similar to the original structure:
export type Css = { [key: string]: any };
export type OctaveFunc<P extends any> = (x: number) => [any, P];
export
interface OptOctave<P extends any> {
func?: OctaveFunc<P>;
min?: number;
length?: number;
}
export
interface Octave<P extends any> extends OptOctave<P> {
func: OctaveFunc<P>;
min: number;
length: number;
}
interface Color {
light: string;
main: string;
dark: string;
contrastText: string;
}
interface Palette {
color: Octave<string>[],
type: string;
primary: Color;
secondary: Color;
success: Color;
warning: Color;
error: Color;
info: Color;
divider: string;
text: {
primary: string;
secondary: string;
},
background: {
paper: string;
default: string;
}
}
export interface Theme {
palette: Palette;
spacing: OptOctave<string>[];
typography: {
htmlFontSize: number;
fontFamily: string;
fontSize: OptOctave<string>[],
h1: Css;
h2: Css;
h3: Css;
h4: Css;
h5: Css;
h6: Css;
subtitle2: Css;
caption: Css;
};
}
The only part in which it differs is the color property of the palette object,
the spacing prop of the theme, and the fontSize property of typography.
These are "Octaves", which means you can provide your own colors, spacings and fontSizes,
and class names will be created for using these.
makeMagic receives an object, bindMagic and useMagic receive an
argument named "getStyle", which returns an object.
This is similar to the callback you pass to makeStyles in MUI v4.
This object will control which styles will be generated.
For example the following call:
makeMagic({
specialCard: {
color: "cyan",
"&:hover": {
color: "blue",
}
},
"!MuiButton-root": {
color: "red",
},
"?body": {
backgroundColor: "gray",
},
}, ".light ");
Will generate the styles:
.light .special-card { color: "cyan"; }
.light .special-card:hover { color: "blue"; }
.light .MuiButton-root { color: "red"; }
body { background-color: "gray"; }
So:
- "&" will be replaced by the parent selector. It is useful for styling things in relation to a parent. It can only be used inside another selector.
- "!" will be replaced by ".". It means literal. It avoids transforming the keys from camel case to dashes.
- "?" means that we will declare a CSS rule exactly as stated after the "?".
- If you leave out the above, the class name will be transformed to dash format (assumed camel case).
You can build this library by using pnpm instead of npm.
Clone this repo, cd into its directory. and "pnpm i".
To build in production mode:
pnpm build
And to start webpack watch:
pnpm watch