1import Screen from "@/components/ui/pxl/screen";2import { SpriteCanvas } from "@/components/ui/pxl/sprite-canvas";3import { WidgetArea } from "@/components/ui/pxl/widget-area";4
5export default function ScreenPreview() {6 return (7 <WidgetArea size="sm">8 <Screen9 className="size-full flex items-center justify-center"10 variant="lcd"11 >12 <Screen.Filter>13 <SpriteCanvas14 src="https://raw.githubusercontent.com/pxl-ui/registry/main/app/public/sprites/tiny_chick.png"15 animation="Idle"16 size="lg"17 atlas={{18 frames: {19 "0": { frame: { x: 0, y: 0, w: 16, h: 16 } },20 "1": { frame: { x: 16, y: 0, w: 16, h: 16 } },21 "2": { frame: { x: 32, y: 0, w: 16, h: 16 } },22 "3": { frame: { x: 48, y: 0, w: 16, h: 16 } },23 },24 meta: { frameTags: [{ name: "Idle", from: 0, to: 3 }] },25 }}26 />27 </Screen.Filter>28 </Screen>29 </WidgetArea>30 );31}Installation
Section titled “Installation”pnpm dlx shadcn@latest add pxl-ui/registry/screen
Copy and paste the following code into your project.
1import { cva, type VariantProps } from "class-variance-authority";2import type { ComponentProps } from "react";3
4import { cn } from "@/lib/utils";5
6const screenVariants = cva("", {7 variants: {8 variant: {9 default: "",10 lcd: "filter-lcd",11 green: "filter-green",12 greenLight: "filter-green-light",13 dark: "filter-dark",14 crt: "filter-crt",15 },16 background: {17 default: "",18 none: "",19 filter: "",20 },21 },22 defaultVariants: {23 variant: "default",24 background: "default",25 },26 compoundVariants: [27 {28 variant: "lcd",29 background: "filter",30 className: "bg-lcd",31 },32 {33 variant: "dark",34 background: "filter",35 className: "bg-lcd-dark",36 },37 {38 variant: "green",39 background: "filter",40 className: "bg-lcd-green",41 },42 {43 variant: "greenLight",44 background: "filter",45 className: "bg-lcd-green-light",46 },47 ],48});49
50function CRTFilter() {51 return (52 <svg xmlns="http://www.w3.org/2000/svg" className="hidden absolute w-0 h-0">53 <defs>54 <filter55 id="crt-filter"56 x="0"57 y="0"58 width="100%"59 height="100%"60 primitiveUnits="userSpaceOnUse"61 colorInterpolationFilters="sRGB"62 >63 <feColorMatrix64 type="matrix"65 in="SourceGraphic"66 result="rChan"67 values="1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0"68 />69 <feOffset in="rChan" dx="-1.2" dy="0" result="rShift" />70
71 <feColorMatrix72 type="matrix"73 in="SourceGraphic"74 result="bChan"75 values="0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0"76 />77 <feOffset in="bChan" dx="1.2" dy="0" result="bShift" />78
79 <feColorMatrix80 type="matrix"81 in="SourceGraphic"82 result="gChan"83 values="0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0"84 />85
86 <feBlend in="rShift" in2="gChan" mode="screen" result="rgBlend" />87 <feBlend in="rgBlend" in2="bShift" mode="screen" result="rgbBlend" />88
89 <feGaussianBlur in="rgbBlend" stdDeviation="2.5" result="glow" />90 <feBlend in="rgbBlend" in2="glow" mode="screen" result="withGlow" />91
92 <feComponentTransfer in="withGlow" result="bright">93 <feFuncR type="linear" slope="1.15" />94 <feFuncG type="linear" slope="1.15" />95 <feFuncB type="linear" slope="1.15" />96 </feComponentTransfer>97 <feColorMatrix98 type="saturate"99 in="bright"100 values="1.30"101 result="saturated"102 />103
104 <feImage105 href="data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'1'%20height%3D'2'%3E%3Crect%20x%3D'0'%20y%3D'1'%20width%3D'1'%20height%3D'1'%20fill%3D'black'%20fill-opacity%3D'0.35'%2F%3E%3C%2Fsvg%3E"106 x="0"107 y="0"108 width="1"109 height="2"110 result="scanTile"111 preserveAspectRatio="none"112 />113 <feTile114 in="scanTile"115 x="0"116 y="0"117 width="160"118 height="160"119 result="scanlines"120 />121 <feBlend122 in="saturated"123 in2="scanlines"124 mode="multiply"125 result="withScan"126 />127
128 <feGaussianBlur in="withScan" stdDeviation="3.2" result="vigBlur" />129 <feComposite130 in="withScan"131 in2="vigBlur"132 operator="arithmetic"133 k1="0"134 k2="1"135 k3="0.08"136 k4="-0.040"137 />138 </filter>139 </defs>140 </svg>141 );142}143
144function GreenLightFilter() {145 return (146 <svg147 width="0"148 height="0"149 className="hidden absolute w-0 h-0"150 aria-hidden="true"151 >152 <filter id="green-light-filter" colorInterpolationFilters="sRGB">153 <feFlood floodColor="hsla(84, 21%, 53%, 0.28)" result="flood" />154 <feComposite155 in="flood"156 in2="SourceGraphic"157 operator="in"158 result="tint"159 />160 <feBlend in="SourceGraphic" in2="tint" mode="multiply" />161 </filter>162 </svg>163 );164}165
166function ScreenProvider({167 background,168 children,169 className,170 variant,171 ...props172}: ComponentProps<"div"> & VariantProps<typeof screenVariants>) {173 return (174 <div175 className={cn(176 className,177 screenVariants({178 background,179 variant,180 }),181 )}182 {...props}183 >184 {variant === "crt" && <CRTFilter />}185 {variant === "greenLight" && <GreenLightFilter />}186 {children}187 </div>188 );189}190
191function ScreenFilter({192 className,193 children,194 ...props195}: ComponentProps<"div">) {196 return (197 <div className={cn("filtered", className)} {...props}>198 {children}199 </div>200 );201}202
203ScreenProvider.Filter = ScreenFilter;204
205export { ScreenFilter, ScreenProvider };206
207export default ScreenProvider;Update the import paths to match your project setup.
import { ScreenFilter, ScreenProvider} from "@/components/ui/pxl/screen";<ScreenProvider variant="lcd"> <ScreenFilter> </ScreenFilter></ScreenProvider>Composition
Section titled “Composition”Use the following composition to build a Screen:
ScreenProvider└── ScreenFilterVariants
Section titled “Variants”1import Screen from "@/components/ui/pxl/screen";2import { SpriteCanvas } from "@/components/ui/pxl/sprite-canvas";3import { WidgetArea } from "@/components/ui/pxl/widget-area";4
5export default function ScreenCRTExample() {6 return (7 <WidgetArea size="sm">8 <Screen9 className="size-full" variant="crt">10 <Screen.Filter className="size-full flex items-center justify-center">11 <SpriteCanvas12 src="https://raw.githubusercontent.com/pxl-ui/registry/main/app/public/sprites/tiny_chick.png"13 animation="Idle"14 size="lg"15 atlas={{16 frames: {17 "0": { frame: { x: 0, y: 0, w: 16, h: 16 } },18 "1": { frame: { x: 16, y: 0, w: 16, h: 16 } },19 "2": { frame: { x: 32, y: 0, w: 16, h: 16 } },20 "3": { frame: { x: 48, y: 0, w: 16, h: 16 } },21 },22 meta: { frameTags: [{ name: "Idle", from: 0, to: 3 }] },23 }}24 />25 </Screen.Filter>26 </Screen>27 </WidgetArea>28 );29}1import { type ComponentProps, useState } from "react";2
3import { Button } from "@/components/ui/pxl/button";4import Screen from "@/components/ui/pxl/screen";5import { SpriteCanvas } from "@/components/ui/pxl/sprite-canvas";6import { WidgetArea } from "@/components/ui/pxl/widget-area";7
8export default function ScreenLCDExample() {9 const [background, setBackground] =10 useState<ComponentProps<typeof Screen>["background"]>("none");11 return (12 <div className="w-full flex flex-col gap-2 items-center justify-center">13 <Button14 onClick={() =>15 setBackground((prev) => (prev === "none" ? "filter" : "none"))16 }17 >18 Toggle Background19 </Button>20 <WidgetArea size="sm">21 <Screen22 className="size-full flex items-center justify-center"23 variant="lcd"24 background={background}25 >26 <Screen.Filter>27 <SpriteCanvas28 src="https://raw.githubusercontent.com/pxl-ui/registry/main/app/public/sprites/tiny_chick.png"29 animation="Idle"30 size="lg"31 atlas={{32 frames: {33 "0": { frame: { x: 0, y: 0, w: 16, h: 16 } },34 "1": { frame: { x: 16, y: 0, w: 16, h: 16 } },35 "2": { frame: { x: 32, y: 0, w: 16, h: 16 } },36 "3": { frame: { x: 48, y: 0, w: 16, h: 16 } },37 },38 meta: { frameTags: [{ name: "Idle", from: 0, to: 3 }] },39 }}40 />41 </Screen.Filter>42 </Screen>43 </WidgetArea>44 </div>45 );46}1import { type ComponentProps, useState } from "react";2
3import { Button } from "@/components/ui/pxl/button";4import Screen from "@/components/ui/pxl/screen";5import { SpriteCanvas } from "@/components/ui/pxl/sprite-canvas";6import { WidgetArea } from "@/components/ui/pxl/widget-area";7
8export default function ScreenDarkExample() {9 const [background, setBackground] =10 useState<ComponentProps<typeof Screen>["background"]>("none");11 return (12 <div className="w-full flex flex-col gap-2 items-center justify-center">13 <Button14 onClick={() =>15 setBackground((prev) => (prev === "none" ? "filter" : "none"))16 }17 >18 Toggle Background19 </Button>20 <WidgetArea size="sm">21 <Screen22 className="size-full flex items-center justify-center"23 variant="dark"24 background={background}25 >26 <Screen.Filter>27 <SpriteCanvas28 src="https://raw.githubusercontent.com/pxl-ui/registry/main/app/public/sprites/tiny_chick.png"29 animation="Idle"30 size="lg"31 atlas={{32 frames: {33 "0": { frame: { x: 0, y: 0, w: 16, h: 16 } },34 "1": { frame: { x: 16, y: 0, w: 16, h: 16 } },35 "2": { frame: { x: 32, y: 0, w: 16, h: 16 } },36 "3": { frame: { x: 48, y: 0, w: 16, h: 16 } },37 },38 meta: { frameTags: [{ name: "Idle", from: 0, to: 3 }] },39 }}40 />41 </Screen.Filter>42 </Screen>43 </WidgetArea>44 </div>45 );46}1import { type ComponentProps, useState } from "react";2
3import { Button } from "@/components/ui/pxl/button";4import Screen from "@/components/ui/pxl/screen";5import { SpriteCanvas } from "@/components/ui/pxl/sprite-canvas";6import { WidgetArea } from "@/components/ui/pxl/widget-area";7
8export default function ScreenGreenExample() {9 const [background, setBackground] =10 useState<ComponentProps<typeof Screen>["background"]>("none");11 return (12 <div className="w-full flex flex-col gap-2 items-center justify-center">13 <Button14 onClick={() =>15 setBackground((prev) => (prev === "none" ? "filter" : "none"))16 }17 >18 Toggle Background19 </Button>20 <WidgetArea size="sm">21 <Screen22 className="size-full flex items-center justify-center"23 variant="green"24 background={background}25 >26 <Screen.Filter>27 <SpriteCanvas28 src="https://raw.githubusercontent.com/pxl-ui/registry/main/app/public/sprites/tiny_chick.png"29 animation="Idle"30 size="lg"31 atlas={{32 frames: {33 "0": { frame: { x: 0, y: 0, w: 16, h: 16 } },34 "1": { frame: { x: 16, y: 0, w: 16, h: 16 } },35 "2": { frame: { x: 32, y: 0, w: 16, h: 16 } },36 "3": { frame: { x: 48, y: 0, w: 16, h: 16 } },37 },38 meta: { frameTags: [{ name: "Idle", from: 0, to: 3 }] },39 }}40 />41 </Screen.Filter>42 </Screen>43 </WidgetArea>44 </div>45 );46}Green Light
Section titled “Green Light”1import { type ComponentProps, useState } from "react";2
3import { Button } from "@/components/ui/pxl/button";4import Screen from "@/components/ui/pxl/screen";5import { SpriteCanvas } from "@/components/ui/pxl/sprite-canvas";6import { WidgetArea } from "@/components/ui/pxl/widget-area";7
8export default function ScreenGreenLightExample() {9 const [background, setBackground] =10 useState<ComponentProps<typeof Screen>["background"]>("none");11 return (12 <div className="w-full flex flex-col gap-2 items-center justify-center">13 <Button14 onClick={() =>15 setBackground((prev) => (prev === "none" ? "filter" : "none"))16 }17 >18 Toggle Background19 </Button>20 <WidgetArea size="sm">21 <Screen22 className="relative size-full flex items-center justify-center"23 variant="greenLight"24 background={background}25 >26 <Screen.Filter>27 <SpriteCanvas28 src="https://raw.githubusercontent.com/pxl-ui/registry/main/app/public/sprites/tiny_chick.png"29 animation="Idle"30 size="lg"31 atlas={{32 frames: {33 "0": { frame: { x: 0, y: 0, w: 16, h: 16 } },34 "1": { frame: { x: 16, y: 0, w: 16, h: 16 } },35 "2": { frame: { x: 32, y: 0, w: 16, h: 16 } },36 "3": { frame: { x: 48, y: 0, w: 16, h: 16 } },37 },38 meta: { frameTags: [{ name: "Idle", from: 0, to: 3 }] },39 }}40 />41 </Screen.Filter>42 </Screen>43 </WidgetArea>44 </div>45 );46}