UNPKG

vite-react-stylesheet

Version:

A stylesheet component, which was created in the aim of finding a better solution to CSS in React Vite.

143 lines (121 loc) 4.88 kB
"use strict"; const StyleSheet = { // Responsive screens. breakpoints: { sm: 576, // Small devices md: 768, // Medium devices lg: 992, // Large devices xl: 1200, // Extra large devices xxl: 1400 // Extra extra large devices }, // Classname generateClassName(key) { return `style-${key}-${Math.random().toString(36).substring(2, 8)}`; }, // Create style create(styles) { let styleSheet = document.getElementById('react-stylesheet'); if (!styleSheet) { styleSheet = document.createElement('style'); styleSheet.id = 'react-stylesheet'; document.head.appendChild(styleSheet); } const classMap = {}; Object.entries(styles).forEach(([styleName, styleObj]) => { const baseStyles = {}; const responsiveStyles = {}; Object.entries(styleObj).forEach(([prop, value]) => { if (prop.startsWith('@') || prop === 'media') { responsiveStyles[prop] = value; } else { baseStyles[prop] = value; } }); const className = this.generateClassName(styleName); classMap[styleName] = className; if (Object.keys(baseStyles).length > 0) { const cssRules = this.toCSSRules(baseStyles); const cssText = `.${className} { ${Object.entries(cssRules) .map(([prop, value]) => `${prop}: ${value};`) .join(' ')} }`; try { styleSheet.sheet.insertRule(cssText, styleSheet.sheet.cssRules.length); } catch (e) { console.error("Error inserting CSS rule:", cssText, e); } } Object.entries(responsiveStyles).forEach(([query, queryStyles]) => { if (query === 'media') { Object.entries(queryStyles).forEach(([mediaQuery, mediaStyles]) => { const mediaCssRules = this.toCSSRules(mediaStyles); const mediaCssText = `@media ${mediaQuery} { .${className} { ${ Object.entries(mediaCssRules) .map(([prop, value]) => `${prop}: ${value};`) .join(' ') } } }`; try { styleSheet.sheet.insertRule(mediaCssText, styleSheet.sheet.cssRules.length); } catch (e) { console.error("Error inserting media CSS rule:", mediaCssText, e); } }); } else if (query.startsWith('@')) { const breakpoint = query.substring(1); if (this.breakpoints[breakpoint]) { const mediaCssRules = this.toCSSRules(queryStyles); const mediaCssText = `@media (min-width: ${this.breakpoints[breakpoint]}px) { .${className} { ${ Object.entries(mediaCssRules) .map(([prop, value]) => `${prop}: ${value};`) .join(' ') } } }`; try { styleSheet.sheet.insertRule(mediaCssText, styleSheet.sheet.cssRules.length); } catch (e) { console.error("Error inserting breakpoint CSS rule:", mediaCssText, e); } } } }); }); return classMap; }, createResponsive(baseStyles, mediaStyles) { const result = {}; Object.entries(baseStyles).forEach(([styleName, styles]) => { result[styleName] = { ...styles }; Object.entries(mediaStyles).forEach(([breakpoint, breakpointStyles]) => { if (breakpointStyles[styleName]) { result[styleName][`@${breakpoint}`] = breakpointStyles[styleName]; } }); }); return this.create(result); }, compose(...args) { return args .filter(Boolean) .map(arg => { if (typeof arg === 'string') return arg; if (typeof arg === 'object') { return Object.entries(arg) .filter(([_, condition]) => Boolean(condition)) .map(([className]) => className); } return null; }) .flat() .filter(Boolean) .join(' '); }, toCSSRules(styles) { return Object.entries(styles).reduce((acc, [prop, value]) => { const cssProperty = prop.replace(/([A-Z])/g, "-$1").toLowerCase(); const cssValue = typeof value === 'number' && !['fontWeight', 'flex', 'opacity', 'zIndex', 'lineHeight'].includes(prop) ? `${value}px` : value; acc[cssProperty] = cssValue; return acc; }, {}); } }; export default StyleSheet;