This document outlines the steps to create BrowserMockup
component styled withTailwind CSS and using some npm dependency libraries.
Node.js
and npm
installed on your machine.Tailwind CSS
installed in your project.CVA(class-variance-authority)
is a utility for managing CSS class names based on various conditions.clsx
is a tiny utility for constructing className strings conditionally.1// browser-mockup.component.tsx
2import clsx from "clsx";
3import {
4 AlignJustifyIcon,
5 ArrowLeftIcon,
6 ArrowRightIcon,
7 CircleUserRoundIcon,
8 DownloadIcon,
9 RotateCwIcon,
10 SearchIcon,
11 XIcon,
12} from "lucide-react";
13
14export interface BrowserMockupProps {
15 variant?: "compact" | "full";
16 href: string;
17 title?: string;
18 hasButtonColor?: boolean;
19 children?: React.ReactNode;
20 className?: string;
21 contentClassName?: string;
22}
23
24const BrowserMockup: React.FC<BrowserMockupProps> = ({
25 variant = "compact",
26 href,
27 title = "Website title",
28 hasButtonColor,
29 className,
30 contentClassName,
31 children,
32}) => {
33 return (
34 <div
35 className={clsx(
36 "min-w-96 overflow-hidden rounded-lg border border-neutral-300 dark:border-neutral-500",
37 className,
38 )}
39 >
40 <div
41 className={clsx("flex items-stretch bg-neutral-200 dark:bg-neutral-800", {
42 "py-3": variant === "compact",
43 "pt-2": variant === "full",
44 })}
45 >
46 <div
47 className={clsx("flex flex-shrink-0 items-center gap-2 pl-6", {
48 "border-b border-neutral-300 dark:border-neutral-500":
49 variant === "full",
50 })}
51 >
52 <div
53 className={clsx(
54 "h-3 w-3 rounded-full",
55 hasButtonColor ? "bg-red-500" : "bg-neutral-400",
56 )}
57 />
58 <div
59 className={clsx(
60 "h-3 w-3 rounded-full",
61 hasButtonColor ? "bg-yellow-500" : "bg-neutral-400",
62 )}
63 />
64 <div
65 className={clsx(
66 "h-3 w-3 rounded-full",
67 hasButtonColor ? "bg-green-500" : "bg-neutral-400",
68 )}
69 />
70 </div>
71 {variant === "compact" ? (
72 <div className="flex flex-1 justify-center px-8">
73 <div className="flex w-full flex-nowrap items-center rounded-lg bg-neutral-50 px-2 py-1 text-neutral-700 dark:bg-neutral-700">
74 <SearchIcon className="mr-2 h-4 w-4 flex-shrink-0" />
75 <p className="flex-1 overflow-hidden text-ellipsis whitespace-nowrap text-sm text-neutral-500 dark:text-neutral-200">
76 {href}
77 </p>
78 </div>
79 </div>
80 ) : (
81 <>
82 <div className="mt-auto h-3 w-6 translate-x-[1px] bg-neutral-50 dark:bg-neutral-700">
83 <div className="h-full w-full rounded-br-lg border-b border-r border-neutral-300 bg-neutral-200 dark:border-neutral-500 dark:bg-neutral-800" />
84 </div>
85 <div className="flex h-9 w-52 items-center rounded-t-lg border-x border-t border-neutral-300 bg-neutral-50 px-4 dark:border-neutral-500 dark:bg-neutral-700">
86 <div className="relative flex-1 overflow-hidden whitespace-nowrap text-sm text-neutral-500 dark:text-neutral-200">
87 {title}
88 <div className="absolute inset-y-0 right-0 w-10 bg-[linear-gradient(to_right,rgba(255,255,255,0),rgba(255,255,255,1))] dark:bg-[linear-gradient(to_right,rgba(64,64,64,0),rgba(64,64,64,1))]" />
89 </div>
90 <div className="flex h-3 w-3 items-center justify-center rounded-full bg-neutral-400">
91 <XIcon className="h-2.5 w-2.5 text-white" />
92 </div>
93 </div>
94 <div className="mt-auto h-3 flex-1 -translate-x-[1px] bg-neutral-50 dark:bg-neutral-700">
95 <div className="h-full w-full rounded-bl-lg border-b border-l border-neutral-300 bg-neutral-200 dark:border-neutral-500 dark:bg-neutral-800" />
96 </div>
97 </>
98 )}
99 </div>
100 {variant === "full" && (
101 <div className="flex w-full flex-1 flex-shrink-0 flex-grow-0 items-center gap-4 bg-neutral-50 px-4 py-2 dark:bg-neutral-700">
102 <div className="flex flex-shrink-0 gap-2 text-neutral-500 dark:text-neutral-200">
103 <ArrowLeftIcon className="h-4 w-4" />
104 <ArrowRightIcon className="h-4 w-4" />
105 <RotateCwIcon className="h-4 w-4" />
106 </div>
107 <div className="w-full flex-1 overflow-hidden text-ellipsis whitespace-nowrap rounded-lg border border-neutral-300 bg-white px-3 py-2 text-sm text-neutral-500 dark:border-neutral-500 dark:bg-neutral-800 dark:text-neutral-200">
108 {href}
109 </div>
110 <div className="flex flex-shrink-0 gap-2 text-neutral-500 dark:text-neutral-200">
111 <DownloadIcon className="h-4 w-4" />
112 <CircleUserRoundIcon className="h-4 w-4" />
113 <AlignJustifyIcon className="h-4 w-4" />
114 </div>
115 </div>
116 )}
117 <div
118 className={clsx(
119 "min-h-32 border-t border-neutral-300 bg-neutral-100 dark:border-neutral-500 dark:bg-neutral-600",
120 contentClassName,
121 )}
122 >
123 {children}
124 </div>
125 </div>
126 );
127};
128
129export default BrowserMockup;
130