Skip to content

Instantly share code, notes, and snippets.

@Cold06
Last active April 2, 2024 00:27
Show Gist options
  • Save Cold06/2629c32bb52bbb7b8364d9b38d948df8 to your computer and use it in GitHub Desktop.
Save Cold06/2629c32bb52bbb7b8364d9b38d948df8 to your computer and use it in GitHub Desktop.
I expect 50K stars by the end of the month
// Of course you can see it live
// https://stackblitz.com/edit/vitejs-vite-sxrsd9?file=src%2FApp.tsx
import * as CSSType from 'csstype';
import {
PropsWithChildren,
createContext,
FC,
useId,
useEffect,
useContext,
useState,
cloneElement,
} from 'react';
type MakeFunnyType<T extends {}> = {
[K in keyof T as K extends string ? Capitalize<K> : K]-?: (props: {
value: T[K];
}) => JSX.Element;
};
type StyleContext = {
addStyle(name: string, value: unknown): void;
removeStyle(name: string): void;
getClassName(): string;
} | null;
const StyleSheetContext = createContext<StyleContext>(null);
const lowerFirst = (str: string) => `${str[0].toLowerCase()}${str.slice(1)}`;
const STYLE_ID = 'CurSSed';
class StylesheetManager {
static style = document.createElement('style');
static {
StylesheetManager.style.id = STYLE_ID;
document.head.appendChild(StylesheetManager.style);
}
static sheet = [...document.styleSheets].find(
({ ownerNode }) => ownerNode instanceof Element && ownerNode.id === STYLE_ID
)!;
static propertyRegistry = new Map<string, number>();
static getRule(id: string): CSSStyleRule {
const existing = [...this.sheet.rules].find(
(rule) =>
rule instanceof CSSStyleRule && rule.selectorText === buildClassName(id)
);
if (existing) return existing as CSSStyleRule;
this.sheet.addRule(buildClassName(id));
// ugly but has to work
return [...this.sheet.rules].find(
(rule) =>
rule instanceof CSSStyleRule && rule.selectorText === buildClassName(id)
) as CSSStyleRule;
}
static addRule(id: string, name: string, value: unknown) {
const rule = this.getRule(id);
// shhhhhhhhh
// @ts-ignore
rule.style[lowerFirst(name)] = value;
}
static removeRule(id: string, name: string) {
const rule = this.getRule(id);
// shhhhhhhhh
// @ts-ignore
delete rule.style[lowerFirst(name)];
}
}
console.log(StylesheetManager, StylesheetManager.sheet.cssRules);
const buildClassName = (id: string) =>
`.${STYLE_ID}-${id}`.replace(/:/g, '__').toLowerCase();
export const CSS: FC<PropsWithChildren> = ({ children }) => {
const id = useId();
const [context] = useState<StyleContext>(() => ({
addStyle(name, value) {
StylesheetManager.addRule(id, name, value);
},
removeStyle(name) {
StylesheetManager.removeRule(id, name);
},
getClassName() {
return buildClassName(id).slice(1);
},
}));
return (
<StyleSheetContext.Provider value={context}>
{Array.isArray(children)
? children.map((child, index) =>
cloneElement(child, {
key: index,
className: context!.getClassName(),
})
)
: // just dont pass strings as child
// @ts-ignore
cloneElement(children, { className: context!.getClassName() })}
</StyleSheetContext.Provider>
);
};
export const useClassName = () => {
const ctx = useContext(StyleSheetContext);
return ctx?.getClassName();
};
export default new Proxy(
{},
{
get(_, p) {
return ({ value }: { value: unknown }) => {
const context = useContext(StyleSheetContext);
useEffect(() => {
context?.addStyle(String(p), value);
return () => {
context?.removeStyle(String(p));
};
}, [context]);
return null;
};
},
}
) as unknown as MakeFunnyType<CSSType.Properties>;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment