# Files changed · PR \#16 · screen/col

[View on GitCafe](https://git.cafe/screen/col/pull/16/diffs)

Repository: [screen/col](https://git.cafe/screen/col)

Visibility: public

[Standardize application controls through shadcn/ui](https://git.cafe/screen/col/pull/16.md)

Version: 0

Head commit: 9ba36a667898502bdff90e202ea191f40295a787

Base commit: 7d2a2221496bb601a3ee86ac7209cc36f8992437

Comparison base: 7d2a2221496bb601a3ee86ac7209cc36f8992437

Showing up to 20 entries from this revision. If the pull request changes, reopen the first page before continuing.

## README.md

Status: modified; +7; −1

````diff
@@ -167,10 +167,16 @@
 public/hero-logos/    Library artwork used by the homepage
 .github/              Issue forms and pull request guidance
 ```
+
+## UI components
+
+Application controls use the components in [`components/ui`](components/ui). Use the existing `Button`, `Input`, `Tabs`, and `DropdownMenu` before adding another control. Keep product-specific layout and behavior in `components/`, and keep visual variants in `components/ui/` when the standard component does not cover them.
 
+`components.json` configures shadcn/ui. The components are owned by this repository and may use Radix primitives internally for keyboard and accessibility behavior. Theme colors for those components live in `app/globals.css`.
+
 ## Built with
 
-[Next.js](https://nextjs.org) · [React](https://react.dev) · [TypeScript](https://www.typescriptlang.org) · [Tailwind CSS](https://tailwindcss.com) · [Radix UI](https://www.radix-ui.com) · [Lucide](https://lucide.dev)
+[Next.js](https://nextjs.org) · [React](https://react.dev) · [TypeScript](https://www.typescriptlang.org) · [Tailwind CSS](https://tailwindcss.com) · [shadcn/ui](https://ui.shadcn.com) · [Radix UI](https://www.radix-ui.com) · [Lucide](https://lucide.dev)
 
 ## Community
 
````

## app/globals.css

Status: modified; +53; −1

```diff
@@ -10,7 +10,6 @@
 
   --color-surface: #000;
   --color-surface-raised: #101010;
-  --color-accent: #fff;
   --color-accent-soft: #fff;
 }
 
@@ -127,6 +126,24 @@
   --col-brand: #0800ff;
   --col-blue: #69a9ff;
   --col-green: #3ddc97;
+  --background: #000;
+  --foreground: #fff;
+  --card: #050505;
+  --card-foreground: #fff;
+  --popover: #101010;
+  --popover-foreground: #fff;
+  --primary: #0800ff;
+  --primary-foreground: #fff;
+  --secondary: #1a1a1a;
+  --secondary-foreground: #fff;
+  --muted: #1a1a1a;
+  --muted-foreground: #b3b3b3;
+  --accent: #202020;
+  --accent-foreground: #fff;
+  --destructive: #dc2626;
+  --border: #303030;
+  --input: #424242;
+  --ring: #69a9ff;
   --color-1: oklch(66.2% 0.225 25.9);
   --color-2: oklch(60.4% 0.26 302);
   --color-3: oklch(69.6% 0.165 251);
@@ -139,6 +156,23 @@
   color: #171717;
 }
 
+html.light {
+  --background: #f7f7f5;
+  --foreground: #171717;
+  --card: #fff;
+  --card-foreground: #171717;
+  --popover: #fff;
+  --popover-foreground: #171717;
+  --secondary: #ededeb;
+  --secondary-foreground: #171717;
+  --muted: #ededeb;
+  --muted-foreground: #525252;
+  --accent: #ededeb;
+  --accent-foreground: #171717;
+  --border: #d4d4d4;
+  --input: #bcbcbc;
+}
+
 .theme-text { color: #fff; }
 .theme-muted { color: rgba(255, 255, 255, 0.68); }
 .theme-border { border-color: rgba(255, 255, 255, 0.12); }
@@ -867,6 +901,24 @@
 }
 
 @theme inline {
+  --color-background: var(--background);
+  --color-foreground: var(--foreground);
+  --color-card: var(--card);
+  --color-card-foreground: var(--card-foreground);
+  --color-popover: var(--popover);
+  --color-popover-foreground: var(--popover-foreground);
+  --color-primary: var(--primary);
+  --color-primary-foreground: var(--primary-foreground);
+  --color-secondary: var(--secondary);
+  --color-secondary-foreground: var(--secondary-foreground);
+  --color-muted: var(--muted);
+  --color-muted-foreground: var(--muted-foreground);
+  --color-accent: var(--accent);
+  --color-accent-foreground: var(--accent-foreground);
+  --color-destructive: var(--destructive);
+  --color-border: var(--border);
+  --color-input: var(--input);
+  --color-ring: var(--ring);
   --animate-shimmer-slide: shimmer-slide var(--speed) ease-in-out infinite alternate;
   --animate-spin-around: spin-around calc(var(--speed) * 2) infinite linear;
   --animate-rainbow: rainbow var(--speed, 2s) infinite linear;
```

## components/CategoryBento.tsx

Status: modified; +5; −3

```diff
@@ -13,6 +13,7 @@
   type LucideIcon,
 } from "lucide-react";
 import { CATEGORIES, libraries, type Category } from "@/data/libraries";
+import { Button } from "@/components/ui/button";
 import GlareHover from "./GlareHover";
 
 const CATEGORY_ICONS: Record<Category, LucideIcon> = {
@@ -53,8 +54,9 @@
               glareSize={180}
               className="group"
             >
-              <button
+              <Button
                 type="button"
+                variant="ghost"
                 onClick={() => {
                   onSelect(isActive ? null : category);
                   document.getElementById("directory")?.scrollIntoView({
@@ -62,7 +64,7 @@
                   });
                 }}
                 aria-pressed={isActive}
-                className="relative z-10 flex h-full w-full flex-col justify-between p-5 text-left"
+                className="relative z-10 flex h-full w-full flex-col items-stretch justify-between whitespace-normal p-5 text-left hover:bg-transparent"
               >
                 <div className="flex items-start justify-between text-white/75">
                   <Icon className="size-5" strokeWidth={1.5} aria-hidden />
@@ -77,7 +79,7 @@
                     {matches.slice(0, 3).map((library) => library.name).join(" · ") || "More soon"}
                   </p>
                 </div>
-              </button>
+              </Button>
             </GlareHover>
           );
         })}
```

## components/DocsSidebar.tsx

Status: modified; +2; −1

```diff
@@ -4,6 +4,7 @@
 import { usePathname } from "next/navigation";
 import Link from "next/link";
 import { ChevronDown, Search } from "lucide-react";
+import { Button } from "@/components/ui/button";
 
 const getStarted = [
   ["/docs", "Overview"],
@@ -65,7 +66,7 @@
 
   return (
     <nav aria-label="Documentation pages" className="docs-sidebar rounded-xl border p-4">
+      <Button type="button" variant="ghost" className="docs-mobile-toggle theme-text min-h-11 w-full justify-between px-0 text-sm hover:bg-transparent" aria-expanded={open} onClick={() => setOpen((current) => !current)}>Documentation menu <ChevronDown className={`size-4 ${open ? "rotate-180" : ""}`} aria-hidden /></Button>
-      <button type="button" className="docs-mobile-toggle theme-text min-h-11 w-full items-center justify-between text-sm font-medium" aria-expanded={open} onClick={() => setOpen((current) => !current)}>Documentation menu <ChevronDown className={`size-4 ${open ? "rotate-180" : ""}`} aria-hidden /></button>
       <div className={`docs-sidebar-content ${open ? "docs-sidebar-content-open" : ""}`}>
       <a href="/libraries#library-search" className="docs-sidebar-search mb-6 flex items-center gap-2 rounded-md border px-3 py-2 text-sm">
         <Search className="size-4" aria-hidden /> Search libraries
```

## components/FilterBar.tsx

Status: modified; +1; −1

```diff
@@ -85,7 +85,7 @@
         <Button type="button" variant="outline" onClick={onClearAll} disabled={!hasFilters} className="coss-trigger"><RotateCcw aria-hidden /> Clear</Button></div>
       </div>
 
-      <button type="button" className="filter-mobile-toggle min-h-11 w-full items-center justify-between text-sm font-medium" aria-expanded={filtersOpen} onClick={() => setFiltersOpen((open) => !open)}>Filters <ChevronDown className={`size-4 ${filtersOpen ? "rotate-180" : ""}`} aria-hidden /></button>
+      <Button type="button" variant="ghost" className="filter-mobile-toggle min-h-11 w-full justify-between px-0 text-sm hover:bg-transparent" aria-expanded={filtersOpen} onClick={() => setFiltersOpen((open) => !open)}>Filters <ChevronDown className={`size-4 ${filtersOpen ? "rotate-180" : ""}`} aria-hidden /></Button>
       <div className={`filter-categories ${filtersOpen ? "filter-categories-open" : ""}`}><Tabs value={activeCategory ?? "all"} onValueChange={(value) => onCategoryChange(value === "all" ? null : value as Category)}>
         <TabsList className="filter-category-list h-auto! w-full flex-nowrap justify-start gap-1 overflow-x-auto rounded-none bg-transparent p-0">
           <TabsTrigger value="all" className="filter-tab min-h-11 flex-none rounded-md px-3">All</TabsTrigger>
```

## components/Header.tsx

Status: modified; +4; −2

```diff
@@ -6,6 +6,7 @@
 import { usePathname } from "next/navigation";
 import { Menu, Search, X } from "lucide-react";
 import { RainbowButton } from "@/components/ui/rainbow-button";
+import { Button } from "@/components/ui/button";
 import { ThemeToggle } from "./ThemeToggle";
 
 const compactNumber = new Intl.NumberFormat("en", { notation: "compact", maximumFractionDigits: 1 });
@@ -178,8 +179,9 @@
           >
             <a href="https://github.com/screen-gd/Col/issues/new" target="_blank" rel="noopener noreferrer">Submit</a>
           </RainbowButton>
+          <Button
-          <button
             type="button"
+            variant="ghost"
             className="site-header-menu"
             aria-label={menuOpen ? "Close menu" : "Open menu"}
             aria-expanded={menuOpen}
@@ -187,7 +189,7 @@
             onClick={() => setMenuOpen((open) => !open)}
           >
             {menuOpen ? <X size={17} aria-hidden="true" /> : <Menu size={17} aria-hidden="true" />}
+          </Button>
-          </button>
         </div>
 
         {menuOpen && (
```

## components/SearchBar.tsx

Status: modified; +6; −4

```diff
@@ -2,6 +2,8 @@
 
 import { useEffect, useRef, type FormEvent } from "react";
 import { ArrowRight, Search } from "lucide-react";
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
 
 interface SearchBarProps {
   value: string;
@@ -53,7 +55,7 @@
       <div className="hero-search-hints" aria-hidden="true">
         {hints.map((hint) => <span key={hint} className="hero-search-hint">{hint}</span>)}
       </div>
+      <Input
-      <input
         ref={inputRef}
         type="search"
         name="q"
@@ -66,12 +68,12 @@
         }}
         placeholder="Search libraries, stacks, or use cases…"
         aria-label="Search UI libraries"
-        className="hero-search-input h-full min-w-0 flex-1 bg-transparent text-[15px] outline-none placeholder:text-transparent focus:placeholder:text-white/45 [&::-webkit-search-cancel-button]:hidden"
+        className="hero-search-input h-full min-w-0 flex-1 border-0 bg-transparent px-0 text-[15px] shadow-none placeholder:text-transparent focus-visible:border-0 focus-visible:ring-0 focus:placeholder:text-white/45 dark:bg-transparent [&::-webkit-search-cancel-button]:hidden"
       />
       <kbd className="hero-search-shortcut hidden shrink-0 rounded-md border px-2 py-1 font-mono text-[10px] tracking-wide sm:block">⌘ K</kbd>
+      <Button type="submit" size="icon" variant="ghost" className="hero-search-submit grid size-9 shrink-0 place-items-center rounded-lg hover:bg-transparent" aria-label="Search libraries">
-      <button type="submit" className="hero-search-submit grid size-9 shrink-0 place-items-center rounded-lg" aria-label="Search libraries">
         <ArrowRight className="size-4" aria-hidden />
-      </button>
+      </Button>
     </form>
   );
 }
```

## components/SiteNotice.tsx

Status: modified; +8; −5

```diff
@@ -1,6 +1,7 @@
 "use client";
 
 import { useEffect, useState } from "react";
+import { Button } from "@/components/ui/button";
 
 const noticeKey = "col:site-notice-dismissed";
 
@@ -34,12 +35,14 @@
           <p className="mt-1 text-sm leading-6">Found an issue or bug? Please report it on GitHub.</p>
         </div>
         <div className="flex shrink-0 flex-wrap items-center gap-x-5 gap-y-2">
+          <Button asChild className="min-h-11 rounded-none bg-[#0800ff] px-4 text-sm font-semibold text-white hover:bg-[#241eff]">
+            <a href="https://github.com/screen-gd/Col/issues/new/choose" target="_blank" rel="noopener noreferrer">
+              Open GitHub issue
+            </a>
+          </Button>
+          <Button type="button" variant="link" onClick={close} className="min-h-11 px-0 text-sm text-inherit underline underline-offset-4">
-          <a href="https://github.com/screen-gd/Col/issues/new/choose" target="_blank" rel="noopener noreferrer" className="inline-flex min-h-11 items-center justify-center bg-[#0800ff] px-4 text-sm font-semibold text-white focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#0800ff]">
-            Open GitHub issue
-          </a>
-          <button type="button" onClick={close} className="min-h-11 text-sm font-medium underline underline-offset-4 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current">
             Close notice
+          </Button>
-          </button>
         </div>
       </div>
     </aside>
```

## components/ThemeToggle.tsx

Status: modified; +4; −2

```diff
@@ -3,6 +3,7 @@
 import { useEffect, useRef, useState } from "react";
 import { flushSync } from "react-dom";
 import { Moon, Sun } from "lucide-react";
+import { Button } from "@/components/ui/button";
 
 type ThemeViewTransition = {
   ready: Promise<void>;
@@ -74,15 +75,16 @@
   };
 
   return (
+    <Button
-    <button
       ref={buttonRef}
       type="button"
+      variant="ghost"
       onClick={toggle}
       aria-label={`Use ${light ? "dark" : "light"} mode`}
       title={light ? "Dark mode" : "Light mode"}
       className="theme-toggle grid size-11 place-items-center rounded-lg border"
     >
       {light ? <Moon className="size-4" aria-hidden /> : <Sun className="size-4" aria-hidden />}
+    </Button>
-    </button>
   );
 }
```

## components/ui/liquid-glass-button.tsx

Status: modified; +1; −39

```diff
@@ -5,44 +5,6 @@
 import { cva, type VariantProps } from "class-variance-authority";
 import { cn } from "@/lib/utils";
 
-const buttonVariants = cva(
-  "inline-flex items-center cursor-pointer justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
-  {
-    variants: {
-      variant: {
-        default: "bg-primary text-primary-foreground hover:bg-primary/90",
-        destructive: "bg-destructive text-primary-foreground hover:bg-destructive/90",
-        cool: "dark:inset-shadow-2xs dark:inset-shadow-white/10 bg-linear-to-t border border-b-2 border-zinc-950/40 from-primary to-primary/85 shadow-md shadow-primary/20 ring-1 ring-inset ring-white/25 transition-[filter] duration-200 hover:brightness-110 active:brightness-90 dark:border-x-0 text-primary-foreground dark:border-t-0 dark:border-primary/50 dark:ring-white/5",
-        outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
-        secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
-        ghost: "hover:bg-accent hover:text-accent-foreground",
-        link: "text-primary underline-offset-4 hover:underline",
-      },
-      size: {
-        default: "h-9 px-4 py-2",
-        sm: "h-8 rounded-md px-3 text-xs",
-        lg: "h-10 rounded-md px-8",
-        icon: "h-9 w-9",
-      },
-    },
-    defaultVariants: { variant: "default", size: "default" },
-  },
-);
-
-export interface ButtonProps
-  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
-    VariantProps<typeof buttonVariants> {
-  asChild?: boolean;
-}
-
-const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
-  ({ className, variant, size, asChild = false, ...props }, ref) => {
-    const Comp = asChild ? Slot : "button";
-    return <Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />;
-  },
-);
-Button.displayName = "Button";
-
 const liquidbuttonVariants = cva(
   "relative inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-xl text-sm font-medium outline-none transition-[color,background-color,box-shadow,transform] duration-500 ease-[cubic-bezier(0.22,1,0.36,1)] disabled:pointer-events-none disabled:opacity-50 focus-visible:ring-[3px] focus-visible:ring-white/70 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
   {
@@ -179,4 +141,4 @@
 );
 MetalButton.displayName = "MetalButton";
 
-export { Button, buttonVariants, liquidbuttonVariants, LiquidButton };
+export { liquidbuttonVariants, LiquidButton };
```
