Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions apps/origin/components/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ const links = [{ href: "/easings", text: "Easing Classes" }];

export default function Header() {
const isMobile = useIsMobile();
const cossHomeUrl = process.env.NEXT_PUBLIC_COSS_URL || "";

return (
<header className="relative mb-14 before:absolute before:-inset-x-32 before:bottom-0 before:h-px before:bg-[linear-gradient(to_right,--theme(--color-border/.3),--theme(--color-border)_200px,--theme(--color-border)_calc(100%-200px),--theme(--color-border/.3))]">
Expand All @@ -20,7 +19,7 @@ export default function Header() {
/>
<div className="mx-auto flex h-[72px] w-full max-w-6xl items-center justify-between gap-3">
<div className="-mt-0.5 flex shrink-0 items-start gap-1.5 font-heading text-2xl sm:text-[1.625em]">
<a aria-label="Home" href={cossHomeUrl}>
<a aria-label="Home" href="/ui">
coss.com
</a>
<Link
Expand Down
31 changes: 20 additions & 11 deletions apps/ui/app/not-found.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,37 @@
import {
PageHeader,
PageHeaderDescription,
PageHeaderHeading,
} from "@coss/ui/shared/page-header";
import { SiteFooter } from "@coss/ui/shared/site-footer";
import { RiArrowLeftLine } from "@remixicon/react";
import type { Metadata } from "next";
import Link from "next/link";
import { Button } from "@/registry/default/ui/button";
import { NotFoundBlueprint } from "@/components/not-found-blueprint";
import { NotFoundMotion } from "@/components/not-found-motion";

export const metadata: Metadata = {
description:
"The page you're looking for doesn't exist or may have been moved.",
"The page you’re looking for doesn’t exist or may have been moved.",
title: "Page Not Found",
};

export default function NotFound() {
return (
<div className="container w-full">
<PageHeader>
<PageHeaderHeading>Page Not Found</PageHeaderHeading>
<PageHeaderDescription>
The page you&apos;re looking for doesn&apos;t exist or may have been
moved.
<>
<section className="container flex w-full flex-1 flex-col items-center gap-5 py-10 text-center [font-optical-sizing:auto] [font-synthesis:none] md:py-14 lg:py-16">
<div className="pointer-events-none w-full max-w-140 select-none lg:-my-12">
<NotFoundMotion>
<NotFoundBlueprint />
</NotFoundMotion>
</div>
<PageHeaderHeading className="text-balance leading-[1.08] [font-variation-settings:'GEOM'_50]">
Page Not Found
</PageHeaderHeading>
<PageHeaderDescription className="max-w-md text-pretty">
The page you’re looking for doesn’t exist or may have been moved.
</PageHeaderDescription>
<div className="mt-4">
<div className="mt-1">
<Button
className="group"
render={
Expand All @@ -38,7 +46,8 @@ export default function NotFound() {
size="lg"
/>
</div>
</PageHeader>
</div>
</section>
<SiteFooter />
</>
);
}
39 changes: 28 additions & 11 deletions apps/ui/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {
PageHeader,
PageHeaderDescription,
PageHeaderHeading,
} from "@coss/ui/shared/page-header";
Expand All @@ -18,27 +17,40 @@ import {
} from "@/registry/default/ui/card";
import { particles } from "@/registry/registry-particles";
import { getCategoryThumbnail } from "@/components/category-thumbnails";
import { PrimitivesBlueprint } from "@/components/primitives-blueprint";
import { PrimitivesMotion } from "@/components/primitives-motion";
import { categories } from "@/config/categories";
import { PAGES_NEW } from "@/lib/docs";

const description = "Built for developers and AI.";
const description =
"Accessible React components we use to build Cal.com, powered by Base UI and styled with Tailwind CSS. Designed for developers and AI, with source code you can make your own.";
const particleCount = particles.length;

export const metadata: Metadata = {
description,
title: "A new, modern UI component library built on top of Base UI - coss ui",
title: "A modern React UI component library – coss ui",
};

export default function Page() {
return (
<>
<div className="container w-full">
<PageHeader className="max-w-2xl *:items-start *:text-left">
<PageHeaderHeading>
A new, modern UI component library built on top of Base UI.
<section className="container grid w-full items-center gap-6 py-10 md:py-14 lg:grid-cols-2 lg:gap-10 lg:py-28 xl:gap-16">
<div className="flex w-full min-w-0 max-w-xl flex-col items-start gap-5 [font-optical-sizing:auto] [font-synthesis:none]">
<PageHeaderHeading className="text-balance text-[clamp(2.25rem,1.5rem+2.6vw,4rem)] leading-none [font-variation-settings:'GEOM'_50] lg:text-[clamp(2.25rem,1.5rem+2.6vw,4rem)]">
A modern React UI component library.
</PageHeaderHeading>
<PageHeaderDescription>{description}</PageHeaderDescription>
<div className="mt-2 flex gap-2">
<PageHeaderDescription>
Accessible React components we use to build{" "}
<Link
className="underline underline-offset-4 hover:text-foreground"
href="https://cal.com"
>
Cal.com
</Link>
, powered by Base UI and styled with Tailwind CSS. Designed for
developers and AI, with source code you can make your own.
</PageHeaderDescription>
<div className="mt-1 flex flex-wrap gap-2">
<Button render={<Link href="/docs" />} size="lg">
Get started
</Button>
Expand All @@ -50,8 +62,13 @@ export default function Page() {
Browse {particleCount} particles
</Button>
</div>
</PageHeader>
</div>
</div>
<div className="pointer-events-none mx-auto hidden w-full max-w-140 select-none lg:-my-24 lg:block">
<PrimitivesMotion>
<PrimitivesBlueprint />
</PrimitivesMotion>
</div>
</section>
<div className="relative before:absolute before:inset-x-0 before:top-0 before:h-px before:bg-border/64">
<div
aria-hidden="true"
Expand Down
56 changes: 39 additions & 17 deletions apps/ui/app/particles/page.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
import {
PageHeader,
PageHeaderDescription,
PageHeaderHeading,
} from "@coss/ui/shared/page-header";
import { SiteFooter } from "@coss/ui/shared/site-footer";
import type { Metadata } from "next";
import { Suspense } from "react";
import { isValidRegistryCategory } from "@/registry/registry-categories";
import { particles } from "@/registry/registry-particles";
import { ParticlesDisplay } from "./particles-display";
import SearchContainer from "./search-container";
import { ParticlesBlueprint } from "@/components/particles-blueprint";
import { ParticlesMotion } from "@/components/particles-motion";

const particleCount = particles.length;

const description = `Discover ${particleCount} ready-to-use particles, the building blocks of your design system. Filter by category to find the perfect component for your project.`;
const description = `Explore ${particleCount} ready-to-use UI patterns built from coss ui components. Copy the code, customize the details, and make them part of your product.`;

export const metadata: Metadata = {
description,
title: "Browse Particles - coss ui",
title: "Build more with Particles – coss ui",
};

async function ParticlesDisplayServer({
Expand Down Expand Up @@ -58,19 +60,39 @@ export default function Page({
searchParams: Promise<{ tags?: string }>;
}) {
return (
<div className="container w-full">
<PageHeader className="*:pb-8!">
<PageHeaderHeading>Browse Particles</PageHeaderHeading>
<PageHeaderDescription className="max-w-2xl">
{description}
</PageHeaderDescription>
</PageHeader>
<Suspense>
<SearchContainer />
</Suspense>
<Suspense>
<ParticlesDisplayServer searchParams={searchParams} />
</Suspense>
</div>
<>
<section className="container grid w-full items-center gap-6 py-10 md:py-14 lg:grid-cols-2 lg:gap-10 lg:py-28 xl:gap-16">
<div className="flex w-full min-w-0 max-w-xl flex-col items-start gap-5 [font-optical-sizing:auto] [font-synthesis:none]">
<PageHeaderHeading className="text-balance text-[clamp(2.25rem,1.5rem+2.6vw,4rem)] leading-none [font-variation-settings:'GEOM'_50] lg:text-[clamp(2.25rem,1.5rem+2.6vw,4rem)]">
Build more with Particles.
</PageHeaderHeading>
<PageHeaderDescription className="text-pretty">
{description}
</PageHeaderDescription>
<div className="mt-2 min-h-12 w-full sm:min-h-11">
<Suspense>
<SearchContainer />
</Suspense>
</div>
</div>
<div className="pointer-events-none mx-auto hidden w-full max-w-140 select-none lg:-my-24 lg:block">
<ParticlesMotion>
<ParticlesBlueprint />
</ParticlesMotion>
</div>
</section>
<div className="relative flex-1 before:absolute before:inset-x-0 before:top-0 before:h-px before:bg-border/64">
<div
aria-hidden="true"
className="container pointer-events-none absolute inset-0 z-50 before:absolute before:top-[-3.5px] before:-left-[11.5px] before:z-1 before:-ml-1 before:size-2 before:rounded-[2px] before:border before:border-border before:bg-popover before:bg-clip-padding before:shadow-xs after:absolute after:top-[-3.5px] after:-right-[11.5px] after:z-1 after:-mr-1 after:size-2 after:rounded-[2px] after:border after:border-border after:bg-background after:bg-clip-padding after:shadow-xs dark:after:bg-clip-border dark:before:bg-clip-border"
/>
<div className="container w-full pt-8 md:pt-12">
<Suspense>
<ParticlesDisplayServer searchParams={searchParams} />
</Suspense>
</div>
</div>
<SiteFooter />
</>
);
}
12 changes: 5 additions & 7 deletions apps/ui/app/particles/search-container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,10 @@ export default function SearchContainer() {
);

return (
<div className="mb-8 md:mb-12 lg:mb-16">
<SearchField
items={searchItems}
onItemsChange={updateSelectedItems}
selectedItems={selectedItems}
/>
</div>
<SearchField
items={searchItems}
onItemsChange={updateSelectedItems}
selectedItems={selectedItems}
/>
);
}
2 changes: 1 addition & 1 deletion apps/ui/app/particles/search-field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ export default function SearchField({
};

return (
<div className="mx-auto max-w-2xl">
<div className="w-full max-w-xl">
<Combobox
aria-label="Filter particles"
autoHighlight
Expand Down
31 changes: 31 additions & 0 deletions apps/ui/components/blueprint-dimensions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
export function HorizontalDimension({
x1,
x2,
y,
label,
labelOffset = 8,
}: {
x1: number;
x2: number;
y: number;
label: string;
labelOffset?: number;
}) {
return (
<>
<path
d={`M${x1} ${y}H${x2} M${x1} ${y - 6}v12 M${x2} ${y - 6}v12 M${x1 - 4} ${y + 4}l8-8 M${x2 - 4} ${y + 4}l8-8`}
/>
<text
fill="currentColor"
fontSize="10"
stroke="none"
textAnchor="middle"
x={(x1 + x2) / 2}
y={y - labelOffset}
>
{label}
</text>
</>
);
}
18 changes: 18 additions & 0 deletions apps/ui/components/blueprint-drawing.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { ReactNode } from "react";

export function BlueprintDrawing({ children }: { children: ReactNode }) {
return (
<svg
aria-hidden="true"
className="h-auto w-full overflow-visible font-sans text-foreground [font-optical-sizing:auto] [font-synthesis:none] [font-variation-settings:'GEOM'_25]"
fill="none"
focusable="false"
stroke="currentColor"
strokeWidth="1"
strokeOpacity="0.8"
viewBox="0 0 560 440"
>
{children}
</svg>
);
}
13 changes: 13 additions & 0 deletions apps/ui/components/blueprint-motion.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.animation {
display: none;
}

@media (prefers-reduced-motion: no-preference) {
.drawing[data-live] .animation {
display: block;
}

.drawing[data-live] .original {
visibility: hidden;
}
}
66 changes: 66 additions & 0 deletions apps/ui/components/blueprint-motion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"use client";

import { type ReactNode, useEffect, useRef, useState } from "react";
import { useMediaQuery } from "@/registry/default/hooks/use-media-query";
import styles from "./blueprint-motion.module.css";

export function BlueprintMotion({
children,
renderAnimation,
}: {
children: ReactNode;
renderAnimation: (paused: boolean) => ReactNode;
}) {
const drawing = useRef<HTMLDivElement>(null);
const animation = useRef<HTMLDivElement>(null);
const [hasEntered, setHasEntered] = useState(false);
const [inView, setInView] = useState(false);
const [pageVisible, setPageVisible] = useState(true);
const reducedMotion = useMediaQuery("(prefers-reduced-motion: reduce)");
const live = hasEntered && !reducedMotion;
const stopped = !inView || !pageVisible || !live;

useEffect(() => {
if (!drawing.current) return;
const observer = new IntersectionObserver(([entry]) => {
if (!entry) return;
setInView(entry.isIntersecting);
if (entry.isIntersecting) setHasEntered(true);
});
observer.observe(drawing.current);
return () => observer.disconnect();
}, []);

useEffect(() => {
const update = () => setPageVisible(!document.hidden);
update();
document.addEventListener("visibilitychange", update);
return () => document.removeEventListener("visibilitychange", update);
}, []);

useEffect(() => {
if (!live) return;
const svg = animation.current?.querySelector("svg");
if (!svg) return;
if (stopped) svg.pauseAnimations();
else svg.unpauseAnimations();
}, [stopped, live]);

return (
<div
className={`relative w-full select-none ${styles.drawing}`}
data-live={live || undefined}
ref={drawing}
>
<div className={`pointer-events-none ${styles.original}`}>{children}</div>
{live && (
<div
className={`pointer-events-none absolute inset-0 ${styles.animation}`}
ref={animation}
>
{renderAnimation(stopped)}
</div>
)}
</div>
);
}
Loading
Loading