[data-slot=section]+[data-slot=section]]:mt-8'
+ )}
+ />
+ )
+}
+
+export function SidebarFooter({ className, ...props }: React.ComponentPropsWithoutRef<'div'>) {
+ return (
+
[data-slot=section]+[data-slot=section]]:mt-2.5'
+ )}
+ />
+ )
+}
+
+export function SidebarSection({ className, ...props }: React.ComponentPropsWithoutRef<'div'>) {
+ let id = useId()
+
+ return (
+
+
+
+ )
+}
+
+export function SidebarDivider({ className, ...props }: React.ComponentPropsWithoutRef<'hr'>) {
+ return
+}
+
+export function SidebarSpacer({ className, ...props }: React.ComponentPropsWithoutRef<'div'>) {
+ return
+}
+
+export function SidebarHeading({ className, ...props }: React.ComponentPropsWithoutRef<'h3'>) {
+ return (
+
+ )
+}
+
+export const SidebarItem = forwardRef(function SidebarItem(
+ {
+ current,
+ className,
+ children,
+ ...props
+ }: { current?: boolean; className?: string; children: React.ReactNode } & (
+ | Omit
+ | Omit, 'as' | 'className'>
+ ),
+ ref: React.ForwardedRef
+) {
+ let classes = clsx(
+ // Base
+ 'flex w-full items-center gap-3 rounded-lg px-2 py-2.5 text-left text-base/6 font-medium text-zinc-950 sm:py-2 sm:text-sm/5',
+ // Leading icon/icon-only
+ '*:data-[slot=icon]:size-6 *:data-[slot=icon]:shrink-0 *:data-[slot=icon]:fill-zinc-500 sm:*:data-[slot=icon]:size-5',
+ // Trailing icon (down chevron or similar)
+ '*:last:data-[slot=icon]:ml-auto *:last:data-[slot=icon]:size-5 sm:*:last:data-[slot=icon]:size-4',
+ // Avatar
+ '*:data-[slot=avatar]:-m-0.5 *:data-[slot=avatar]:size-7 sm:*:data-[slot=avatar]:size-6',
+ // Hover
+ 'data-hover:bg-zinc-950/5 data-hover:*:data-[slot=icon]:fill-zinc-950',
+ // Active
+ 'data-active:bg-zinc-950/5 data-active:*:data-[slot=icon]:fill-zinc-950',
+ // Current
+ 'data-current:*:data-[slot=icon]:fill-zinc-950',
+ // Dark mode
+ 'dark:text-white dark:*:data-[slot=icon]:fill-zinc-400',
+ 'dark:data-hover:bg-white/5 dark:data-hover:*:data-[slot=icon]:fill-white',
+ 'dark:data-active:bg-white/5 dark:data-active:*:data-[slot=icon]:fill-white',
+ 'dark:data-current:*:data-[slot=icon]:fill-white'
+ )
+
+ return (
+
+ {current && (
+
+ )}
+ {'href' in props ? (
+
+ {children}
+
+ ) : (
+
+ {children}
+
+ )}
+
+ )
+})
+
+export function SidebarLabel({ className, ...props }: React.ComponentPropsWithoutRef<'span'>) {
+ return
+}
diff --git a/src/components/stacked-layout.tsx b/src/components/stacked-layout.tsx
new file mode 100644
index 0000000..5b1c40e
--- /dev/null
+++ b/src/components/stacked-layout.tsx
@@ -0,0 +1,79 @@
+'use client'
+
+import * as Headless from '@headlessui/react'
+import React, { useState } from 'react'
+import { NavbarItem } from './navbar'
+
+function OpenMenuIcon() {
+ return (
+
+ )
+}
+
+function CloseMenuIcon() {
+ return (
+
+ )
+}
+
+function MobileSidebar({ open, close, children }: React.PropsWithChildren<{ open: boolean; close: () => void }>) {
+ return (
+
+
+
+
+
+
+ )
+}
+
+export function StackedLayout({
+ navbar,
+ sidebar,
+ children,
+}: React.PropsWithChildren<{ navbar: React.ReactNode; sidebar: React.ReactNode }>) {
+ let [showSidebar, setShowSidebar] = useState(false)
+
+ return (
+
+ {/* Sidebar on mobile */}
+
setShowSidebar(false)}>
+ {sidebar}
+
+
+ {/* Navbar */}
+
+
+ {/* Content */}
+
+
+
+
+ )
+}
diff --git a/src/components/switch.tsx b/src/components/switch.tsx
new file mode 100644
index 0000000..4b6b787
--- /dev/null
+++ b/src/components/switch.tsx
@@ -0,0 +1,195 @@
+import * as Headless from '@headlessui/react'
+import clsx from 'clsx'
+import type React from 'react'
+
+export function SwitchGroup({ className, ...props }: React.ComponentPropsWithoutRef<'div'>) {
+ return (
+
+ )
+}
+
+export function SwitchField({
+ className,
+ ...props
+}: { className?: string } & Omit) {
+ return (
+
+ )
+}
+
+const colors = {
+ 'dark/zinc': [
+ '[--switch-bg-ring:var(--color-zinc-950)]/90 [--switch-bg:var(--color-zinc-900)] dark:[--switch-bg-ring:transparent] dark:[--switch-bg:var(--color-white)]/25',
+ '[--switch-ring:var(--color-zinc-950)]/90 [--switch-shadow:var(--color-black)]/10 [--switch:white] dark:[--switch-ring:var(--color-zinc-700)]/90',
+ ],
+ 'dark/white': [
+ '[--switch-bg-ring:var(--color-zinc-950)]/90 [--switch-bg:var(--color-zinc-900)] dark:[--switch-bg-ring:transparent] dark:[--switch-bg:var(--color-white)]',
+ '[--switch-ring:var(--color-zinc-950)]/90 [--switch-shadow:var(--color-black)]/10 [--switch:white] dark:[--switch-ring:transparent] dark:[--switch:var(--color-zinc-900)]',
+ ],
+ dark: [
+ '[--switch-bg-ring:var(--color-zinc-950)]/90 [--switch-bg:var(--color-zinc-900)] dark:[--switch-bg-ring:var(--color-white)]/15',
+ '[--switch-ring:var(--color-zinc-950)]/90 [--switch-shadow:var(--color-black)]/10 [--switch:white]',
+ ],
+ zinc: [
+ '[--switch-bg-ring:var(--color-zinc-700)]/90 [--switch-bg:var(--color-zinc-600)] dark:[--switch-bg-ring:transparent]',
+ '[--switch-shadow:var(--color-black)]/10 [--switch:white] [--switch-ring:var(--color-zinc-700)]/90',
+ ],
+ white: [
+ '[--switch-bg-ring:var(--color-black)]/15 [--switch-bg:white] dark:[--switch-bg-ring:transparent]',
+ '[--switch-shadow:var(--color-black)]/10 [--switch-ring:transparent] [--switch:var(--color-zinc-950)]',
+ ],
+ red: [
+ '[--switch-bg-ring:var(--color-red-700)]/90 [--switch-bg:var(--color-red-600)] dark:[--switch-bg-ring:transparent]',
+ '[--switch:white] [--switch-ring:var(--color-red-700)]/90 [--switch-shadow:var(--color-red-900)]/20',
+ ],
+ orange: [
+ '[--switch-bg-ring:var(--color-orange-600)]/90 [--switch-bg:var(--color-orange-500)] dark:[--switch-bg-ring:transparent]',
+ '[--switch:white] [--switch-ring:var(--color-orange-600)]/90 [--switch-shadow:var(--color-orange-900)]/20',
+ ],
+ amber: [
+ '[--switch-bg-ring:var(--color-amber-500)]/80 [--switch-bg:var(--color-amber-400)] dark:[--switch-bg-ring:transparent]',
+ '[--switch-ring:transparent] [--switch-shadow:transparent] [--switch:var(--color-amber-950)]',
+ ],
+ yellow: [
+ '[--switch-bg-ring:var(--color-yellow-400)]/80 [--switch-bg:var(--color-yellow-300)] dark:[--switch-bg-ring:transparent]',
+ '[--switch-ring:transparent] [--switch-shadow:transparent] [--switch:var(--color-yellow-950)]',
+ ],
+ lime: [
+ '[--switch-bg-ring:var(--color-lime-400)]/80 [--switch-bg:var(--color-lime-300)] dark:[--switch-bg-ring:transparent]',
+ '[--switch-ring:transparent] [--switch-shadow:transparent] [--switch:var(--color-lime-950)]',
+ ],
+ green: [
+ '[--switch-bg-ring:var(--color-green-700)]/90 [--switch-bg:var(--color-green-600)] dark:[--switch-bg-ring:transparent]',
+ '[--switch:white] [--switch-ring:var(--color-green-700)]/90 [--switch-shadow:var(--color-green-900)]/20',
+ ],
+ emerald: [
+ '[--switch-bg-ring:var(--color-emerald-600)]/90 [--switch-bg:var(--color-emerald-500)] dark:[--switch-bg-ring:transparent]',
+ '[--switch:white] [--switch-ring:var(--color-emerald-600)]/90 [--switch-shadow:var(--color-emerald-900)]/20',
+ ],
+ teal: [
+ '[--switch-bg-ring:var(--color-teal-700)]/90 [--switch-bg:var(--color-teal-600)] dark:[--switch-bg-ring:transparent]',
+ '[--switch:white] [--switch-ring:var(--color-teal-700)]/90 [--switch-shadow:var(--color-teal-900)]/20',
+ ],
+ cyan: [
+ '[--switch-bg-ring:var(--color-cyan-400)]/80 [--switch-bg:var(--color-cyan-300)] dark:[--switch-bg-ring:transparent]',
+ '[--switch-ring:transparent] [--switch-shadow:transparent] [--switch:var(--color-cyan-950)]',
+ ],
+ sky: [
+ '[--switch-bg-ring:var(--color-sky-600)]/80 [--switch-bg:var(--color-sky-500)] dark:[--switch-bg-ring:transparent]',
+ '[--switch:white] [--switch-ring:var(--color-sky-600)]/80 [--switch-shadow:var(--color-sky-900)]/20',
+ ],
+ blue: [
+ '[--switch-bg-ring:var(--color-blue-700)]/90 [--switch-bg:var(--color-blue-600)] dark:[--switch-bg-ring:transparent]',
+ '[--switch:white] [--switch-ring:var(--color-blue-700)]/90 [--switch-shadow:var(--color-blue-900)]/20',
+ ],
+ indigo: [
+ '[--switch-bg-ring:var(--color-indigo-600)]/90 [--switch-bg:var(--color-indigo-500)] dark:[--switch-bg-ring:transparent]',
+ '[--switch:white] [--switch-ring:var(--color-indigo-600)]/90 [--switch-shadow:var(--color-indigo-900)]/20',
+ ],
+ violet: [
+ '[--switch-bg-ring:var(--color-violet-600)]/90 [--switch-bg:var(--color-violet-500)] dark:[--switch-bg-ring:transparent]',
+ '[--switch:white] [--switch-ring:var(--color-violet-600)]/90 [--switch-shadow:var(--color-violet-900)]/20',
+ ],
+ purple: [
+ '[--switch-bg-ring:var(--color-purple-600)]/90 [--switch-bg:var(--color-purple-500)] dark:[--switch-bg-ring:transparent]',
+ '[--switch:white] [--switch-ring:var(--color-purple-600)]/90 [--switch-shadow:var(--color-purple-900)]/20',
+ ],
+ fuchsia: [
+ '[--switch-bg-ring:var(--color-fuchsia-600)]/90 [--switch-bg:var(--color-fuchsia-500)] dark:[--switch-bg-ring:transparent]',
+ '[--switch:white] [--switch-ring:var(--color-fuchsia-600)]/90 [--switch-shadow:var(--color-fuchsia-900)]/20',
+ ],
+ pink: [
+ '[--switch-bg-ring:var(--color-pink-600)]/90 [--switch-bg:var(--color-pink-500)] dark:[--switch-bg-ring:transparent]',
+ '[--switch:white] [--switch-ring:var(--color-pink-600)]/90 [--switch-shadow:var(--color-pink-900)]/20',
+ ],
+ rose: [
+ '[--switch-bg-ring:var(--color-rose-600)]/90 [--switch-bg:var(--color-rose-500)] dark:[--switch-bg-ring:transparent]',
+ '[--switch:white] [--switch-ring:var(--color-rose-600)]/90 [--switch-shadow:var(--color-rose-900)]/20',
+ ],
+}
+
+type Color = keyof typeof colors
+
+export function Switch({
+ color = 'dark/zinc',
+ className,
+ ...props
+}: {
+ color?: Color
+ className?: string
+} & Omit) {
+ return (
+
+
+
+ )
+}
diff --git a/src/components/table.tsx b/src/components/table.tsx
new file mode 100644
index 0000000..d944623
--- /dev/null
+++ b/src/components/table.tsx
@@ -0,0 +1,124 @@
+'use client'
+
+import clsx from 'clsx'
+import type React from 'react'
+import { createContext, useContext, useState } from 'react'
+import { Link } from './link'
+
+const TableContext = createContext<{ bleed: boolean; dense: boolean; grid: boolean; striped: boolean }>({
+ bleed: false,
+ dense: false,
+ grid: false,
+ striped: false,
+})
+
+export function Table({
+ bleed = false,
+ dense = false,
+ grid = false,
+ striped = false,
+ className,
+ children,
+ ...props
+}: { bleed?: boolean; dense?: boolean; grid?: boolean; striped?: boolean } & React.ComponentPropsWithoutRef<'div'>) {
+ return (
+ }>
+
+
+ )
+}
+
+export function TableHead({ className, ...props }: React.ComponentPropsWithoutRef<'thead'>) {
+ return
+}
+
+export function TableBody(props: React.ComponentPropsWithoutRef<'tbody'>) {
+ return
+}
+
+const TableRowContext = createContext<{ href?: string; target?: string; title?: string }>({
+ href: undefined,
+ target: undefined,
+ title: undefined,
+})
+
+export function TableRow({
+ href,
+ target,
+ title,
+ className,
+ ...props
+}: { href?: string; target?: string; title?: string } & React.ComponentPropsWithoutRef<'tr'>) {
+ let { striped } = useContext(TableContext)
+
+ return (
+ }>
+
+
+ )
+}
+
+export function TableHeader({ className, ...props }: React.ComponentPropsWithoutRef<'th'>) {
+ let { bleed, grid } = useContext(TableContext)
+
+ return (
+ |
+ )
+}
+
+export function TableCell({ className, children, ...props }: React.ComponentPropsWithoutRef<'td'>) {
+ let { bleed, dense, grid, striped } = useContext(TableContext)
+ let { href, target, title } = useContext(TableRowContext)
+ let [cellRef, setCellRef] = useState(null)
+
+ return (
+
+ {href && (
+
+ )}
+ {children}
+ |
+ )
+}
diff --git a/src/components/text.tsx b/src/components/text.tsx
new file mode 100644
index 0000000..95b1e5d
--- /dev/null
+++ b/src/components/text.tsx
@@ -0,0 +1,40 @@
+import clsx from 'clsx'
+import { Link } from './link'
+
+export function Text({ className, ...props }: React.ComponentPropsWithoutRef<'p'>) {
+ return (
+
+ )
+}
+
+export function TextLink({ className, ...props }: React.ComponentPropsWithoutRef) {
+ return (
+
+ )
+}
+
+export function Strong({ className, ...props }: React.ComponentPropsWithoutRef<'strong'>) {
+ return
+}
+
+export function Code({ className, ...props }: React.ComponentPropsWithoutRef<'code'>) {
+ return (
+
+ )
+}
diff --git a/src/components/textarea.tsx b/src/components/textarea.tsx
new file mode 100644
index 0000000..fc16f88
--- /dev/null
+++ b/src/components/textarea.tsx
@@ -0,0 +1,54 @@
+import * as Headless from '@headlessui/react'
+import clsx from 'clsx'
+import React, { forwardRef } from 'react'
+
+export const Textarea = forwardRef(function Textarea(
+ {
+ className,
+ resizable = true,
+ ...props
+ }: { className?: string; resizable?: boolean } & Omit,
+ ref: React.ForwardedRef
+) {
+ return (
+
+
+
+ )
+})