Dstyler - Dynamic Stylesheets
Dstyler aims to provide a very simple and intuitive API to work with the little known Stylesheet browser API to create stylesheets that react to user input. Create, Remove, Update and Delete css styles,classes and media queries dynamically.
Dstyler should be particularly useful if you are creating a page builder or some other service where you want to allow a user to be able to access the full power of CSS.
Dstyler is "kind of" like the react of stylesheets. To avoid unnecesary dom updates we mantain a virtual cssom (powered by postcss) and only update it when necesary. Minimizing dom updates & screen rerenders.
Getting Started
Install dstyler via npm
npm i dstyler
import { createDynamicStylesheet, dsToJson } from "dstyler";
const initialState = {
div: {
background: "red",
},
};
const ds = createDynamicStylesheet({
id: "stylesheet id",
doc: document,
initialState,
});
ds.media("(min-width: 640px)")
.selector(".my-mobile-class")
.set({ background: "red" });
A word of warning!
This is a very early library, the api's might change fast. I am currently working on adding a few more methods and react support.
API
CreateDynamicStylesheet
export type CreateDynamicStylesheet = ({
id,
doc,
initialState,
}: {
id: string; // unique stylesheet id
doc: Document | null; // required to mount stylesheet & for styles to work on page
initialState?: postcss.CssInJs; // initial state in postcss cssInJs format {".myclass": {background: "red"}}
}) => DynamicStylesheet;
Dynamic Stylesheet
export interface DynamicStylesheet {
media: (params: string) => this;
selector: (cssSelector: string) => this;
set: (values: cssInJs) => void;
setForce: (values: cssInJs) => void;
add: (values: cssInJs) => void;
delete: () => void;
get: () => cssInJs;
updateDocument: (doc: Document) => void;
_ast: any;
}
Method's explanation
Selector methods
ds.media("media query params");
ds.selector("css selector");
Action methods
Get: Returns a css in js object for the nodes selected;
ds.selector("div").get();
set(values): sets the node to the provided value
The set function may be used to set nodes but this is not recomended as it may lead to unsupported behaviours -> ds.set({"div": {background: "red"}})
ds.selector("div").set({
background: "red",
color: "white",
"font-size": "16px",
});
ds.selector("div").set({ background: "blue", color: "white" });
ds.selector("div").set({ background: "blue", color: "white" });
ds.media("(max-width: 300px)").set({ div: { background: "red" } });
add(values): adds value to a node (creates if it doesn't exist, updates if it does)
ds.selector("div").add({ background: "red" });
ds.selector("div").add({ background: "blue" });
ds.media("(max-width: 300px)").add({ div: { background: "red" } });
ds.media(m).add({ div: { backgrund: "red" }, body: { background: "blue" } });
delete(): delete a node and its child nodes
ds.selector("div").delete();
ds.media("(max-width: 300px)").delete();
Opt out of diffing
This can be dangerous if you do not know what you are doing. Be warned.
setForce(values): Set node to provided value
If node doesn't exist it will be created. If it exists, the entire node will be nuked and recreated.
ds.media("(max-width: 300px)").setForce({ body: { background: "red" } });
Mounting the Stylesheet on the DOM & Changing the mounted dom
ds.updateDocument(doc): this is specially useful for working with iframes and such. See my section on integration with react
import { createDynamicStylesheet, dsToJson } from "dstyler";
const ds = createDynamicStylesheet({ id: "id", doc: document });
const ds2 = createDynamicStylesheet({ id: "id" });
ds2.selector(".myclass").set({ background: "red" });
ds2.updateDocument(document);
Storing and Recovering the css
import { dsToJson, createDynamicStylesheet } from "dstyler";
const jsonCSS = dsToJson(ds);
const ds2 = createDynamicStylesheet({
id: "id",
doc: document,
initialState: JSON.parse(jsonCSS),
});
const cssString = ds2._ast.toString();
Future improvements
- improve diffing algorithm to delete nodes if no children
- react compatibility?
Thank You's & Maintenance
Big thank you to @prevwong the maintainer of craftjs for a conversation which led me to the idea for this library.
It is maintained by me @Oudwins as part of my work to develop a website builder for Ridaly. Feel free contribute through the github, open an issue or a PR.
If you need to get in touch with me you can do so through my site