UNPKG

tailwind-screen-size

Version:

A framework-agnostic screen size indicator optimized for TailwindCSS but customizable for any CSS framework. Features real-time dimensions, breakpoints, themes, and development tools.

364 lines (361 loc) 10.1 kB
// src/react/TailwindScreenSize.tsx import React, { useEffect, useState, useMemo } from "react"; import { twMerge } from "tailwind-merge"; // src/utils/index.ts function detectTailwind() { var _a; if (typeof window === "undefined") return true; const tailwindClasses = [ "sm:", "md:", "lg:", "xl:", "2xl:", "flex", "grid", "container", "px-", "py-", "mx-", "my-", "bg-", "text-", "border-" ]; const styleSheets = Array.from(document.styleSheets); try { for (const sheet of styleSheets) { try { if (!((_a = sheet.href) == null ? void 0 : _a.includes(window.location.origin))) continue; const rules = Array.from(sheet.cssRules); for (const rule of rules) { const ruleText = rule.cssText; if (tailwindClasses.some((cls) => ruleText.includes(cls))) { return true; } if (ruleText.includes("@tailwind") || ruleText.includes("@layer") || ruleText.includes("@apply")) { return true; } } } catch (e) { continue; } } const allElements = Array.from(document.getElementsByTagName("*")); for (const element of allElements) { if (tailwindClasses.some( (cls) => Array.from(element.classList).some( (className) => className.includes(cls.replace(":", "-")) ) )) { return true; } } return false; } catch (e) { console.warn("Error detecting Tailwind CSS"); return true; } } function showTailwindWarning() { console.warn( `%cTailwind Screen Size Warning: Tailwind CSS not detected. This component is designed to work with Tailwind CSS. Please ensure you have Tailwind CSS properly installed in your project. Visit https://tailwindcss.com/docs/installation for installation instructions.`, "color: #f59e0b; font-weight: bold;" ); } // src/react/TailwindScreenSize.tsx var positionClasses = { "top-left": "top-5 left-5", "top-right": "top-5 right-5", "bottom-left": "bottom-5 left-5", "bottom-right": "bottom-5 right-5", "top-center": "top-5 left-1/2 -translate-x-1/2", "bottom-center": "bottom-5 left-1/2 -translate-x-1/2" }; var themeClasses = { dark: { container: "bg-gray-900/90 text-white", text: "text-white", divider: "bg-gray-700", breakpoint: "text-gray-400" }, light: { container: "bg-white/90 text-gray-900", text: "text-gray-900", divider: "bg-gray-200", breakpoint: "text-gray-600" }, glass: { container: "bg-white/10 backdrop-blur-md border border-white/20 shadow-lg", text: "text-white", divider: "bg-white/20", breakpoint: "text-white/80" }, minimal: { container: "bg-transparent", text: "text-gray-600 dark:text-gray-300", divider: "bg-gray-300 dark:bg-gray-700", breakpoint: "text-gray-500 dark:text-gray-400" }, slate: { container: "bg-slate-600/90 text-white", text: "text-white", divider: "bg-slate-400", breakpoint: "text-slate-200" }, gray: { container: "bg-gray-600/90 text-white", text: "text-white", divider: "bg-gray-400", breakpoint: "text-gray-200" }, zinc: { container: "bg-zinc-600/90 text-white", text: "text-white", divider: "bg-zinc-400", breakpoint: "text-zinc-200" }, neutral: { container: "bg-neutral-600/90 text-white", text: "text-white", divider: "bg-neutral-400", breakpoint: "text-neutral-200" }, stone: { container: "bg-stone-600/90 text-white", text: "text-white", divider: "bg-stone-400", breakpoint: "text-stone-200" }, red: { container: "bg-red-600/90 text-white", text: "text-white", divider: "bg-red-400", breakpoint: "text-red-200" }, orange: { container: "bg-orange-600/90 text-white", text: "text-white", divider: "bg-orange-400", breakpoint: "text-orange-200" }, amber: { container: "bg-amber-600/90 text-white", text: "text-white", divider: "bg-amber-400", breakpoint: "text-amber-200" }, yellow: { container: "bg-yellow-500/90 text-white", text: "text-white", divider: "bg-yellow-300", breakpoint: "text-yellow-100" }, lime: { container: "bg-lime-600/90 text-white", text: "text-white", divider: "bg-lime-400", breakpoint: "text-lime-200" }, green: { container: "bg-green-600/90 text-white", text: "text-white", divider: "bg-green-400", breakpoint: "text-green-200" }, emerald: { container: "bg-emerald-600/90 text-white", text: "text-white", divider: "bg-emerald-400", breakpoint: "text-emerald-200" }, teal: { container: "bg-teal-600/90 text-white", text: "text-white", divider: "bg-teal-400", breakpoint: "text-teal-200" }, cyan: { container: "bg-cyan-600/90 text-white", text: "text-white", divider: "bg-cyan-400", breakpoint: "text-cyan-200" }, sky: { container: "bg-sky-600/90 text-white", text: "text-white", divider: "bg-sky-400", breakpoint: "text-sky-200" }, blue: { container: "bg-blue-600/90 text-white", text: "text-white", divider: "bg-blue-400", breakpoint: "text-blue-200" }, indigo: { container: "bg-indigo-600/90 text-white", text: "text-white", divider: "bg-indigo-400", breakpoint: "text-indigo-200" }, violet: { container: "bg-violet-600/90 text-white", text: "text-white", divider: "bg-violet-400", breakpoint: "text-violet-200" }, purple: { container: "bg-purple-600/90 text-white", text: "text-white", divider: "bg-purple-400", breakpoint: "text-purple-200" }, fuchsia: { container: "bg-fuchsia-600/90 text-white", text: "text-white", divider: "bg-fuchsia-400", breakpoint: "text-fuchsia-200" }, pink: { container: "bg-pink-600/90 text-white", text: "text-white", divider: "bg-pink-400", breakpoint: "text-pink-200" }, rose: { container: "bg-rose-600/90 text-white", text: "text-white", divider: "bg-rose-400", breakpoint: "text-rose-200" } }; var defaultBreakpoints = [ { screenTitle: "XS", minWidth: 0 }, { screenTitle: "SM", minWidth: 640 }, { screenTitle: "MD", minWidth: 768 }, { screenTitle: "LG", minWidth: 1024 }, { screenTitle: "XL", minWidth: 1280 }, { screenTitle: "2XL", minWidth: 1536 } ]; var isDevelopment = () => { if (typeof process === "undefined") return false; return process.env.NODE_ENV === "development"; }; var baseClasses = { container: "fixed flex items-center gap-2 rounded-full px-2.5 py-1 font-mono text-xs font-medium transition-all duration-200", divider: "h-4 w-px" }; var TailwindScreenSize = ({ className = "", position = "bottom-right", theme = "dark", show, containerClassName = "", textClassName = "", dividerClassName = "", breakpointClassName = "", breakpoints, showDefaultBreakpoints = true, hideNoTailwindCSSWarning = false }) => { const [mounted, setMounted] = useState(false); const [dimensions, setDimensions] = useState(null); const [currentBreakpoint, setCurrentBreakpoint] = useState(""); const [hasTailwind, setHasTailwind] = useState(true); const [isDevMode, setIsDevMode] = useState(false); const allBreakpoints = useMemo(() => { const customBreakpoints = breakpoints || []; if (!showDefaultBreakpoints) return customBreakpoints; return [...defaultBreakpoints, ...customBreakpoints].sort( (a, b) => a.minWidth - b.minWidth ); }, [breakpoints, showDefaultBreakpoints]); useEffect(() => { setMounted(true); setIsDevMode(isDevelopment()); if (typeof window !== "undefined") { const tailwindDetected = detectTailwind(); setHasTailwind(tailwindDetected); } }, []); useEffect(() => { if (!mounted) return; const updateDimensions = () => { setDimensions({ width: window.innerWidth, height: window.innerHeight }); }; updateDimensions(); window.addEventListener("resize", updateDimensions); return () => window.removeEventListener("resize", updateDimensions); }, [mounted]); useEffect(() => { if (mounted && dimensions) { const current = allBreakpoints.slice().reverse().find((breakpoint) => dimensions.width >= breakpoint.minWidth); setCurrentBreakpoint((current == null ? void 0 : current.screenTitle) || ""); } }, [dimensions, allBreakpoints, mounted]); if (!mounted || !dimensions) return null; if (show === false) return null; if (!isDevMode && show !== true) return null; const { width, height } = dimensions; const themeStyles = themeClasses[theme]; return /* @__PURE__ */ React.createElement( "div", { className: twMerge( baseClasses.container, positionClasses[position], themeStyles.container, containerClassName, className ) }, /* @__PURE__ */ React.createElement("span", { className: twMerge(themeStyles.text, textClassName) }, width.toLocaleString(), " \xD7 ", height.toLocaleString()), /* @__PURE__ */ React.createElement( "div", { className: twMerge( baseClasses.divider, themeStyles.divider, dividerClassName ) } ), /* @__PURE__ */ React.createElement("span", { className: twMerge(themeStyles.breakpoint, breakpointClassName) }, currentBreakpoint), !hasTailwind && !hideNoTailwindCSSWarning && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement( "div", { className: twMerge( baseClasses.divider, themeStyles.divider, dividerClassName ) } ), /* @__PURE__ */ React.createElement( "span", { className: twMerge(themeStyles.breakpoint, breakpointClassName) }, "No TailwindCSS Detected" )) ); }; export { TailwindScreenSize, detectTailwind, showTailwindWarning };