@silicajs/ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,13 @@
1
+ import { cn } from "@silicajs/ui/lib/utils"
2
+
3
+ function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
4
+ return (
5
+ <div
6
+ data-slot="skeleton"
7
+ className={cn("animate-pulse rounded-md bg-muted", className)}
8
+ {...props}
9
+ />
10
+ )
11
+ }
12
+
13
+ export { Skeleton }
@@ -0,0 +1,33 @@
1
+ import * as React from "react";
2
+
3
+ import { cn } from "@silicajs/ui/lib/utils";
4
+ import { Badge } from "@silicajs/ui/components/badge";
5
+
6
+ export type TagBadgeProps = {
7
+ tag: string;
8
+ href: string;
9
+ className?: string;
10
+ render?: React.ReactElement;
11
+ };
12
+
13
+ export function TagBadge({ tag, href, className, render }: TagBadgeProps) {
14
+ return (
15
+ <Badge
16
+ variant="outline"
17
+ className={cn("hover:border-primary/40", className)}
18
+ render={
19
+ render ?? (
20
+ <a
21
+ href={href}
22
+ className="cursor-pointer text-foreground/80 no-underline transition-colors hover:text-foreground"
23
+ />
24
+ )
25
+ }
26
+ >
27
+ <span aria-hidden="true" className="text-muted-foreground">
28
+ #
29
+ </span>
30
+ <span>{tag}</span>
31
+ </Badge>
32
+ );
33
+ }
@@ -0,0 +1,18 @@
1
+ import * as React from "react"
2
+
3
+ import { cn } from "@silicajs/ui/lib/utils"
4
+
5
+ function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
6
+ return (
7
+ <textarea
8
+ data-slot="textarea"
9
+ className={cn(
10
+ "flex field-sizing-content min-h-16 w-full rounded-md border border-input bg-transparent px-2.5 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
11
+ className
12
+ )}
13
+ {...props}
14
+ />
15
+ )
16
+ }
17
+
18
+ export { Textarea }
@@ -0,0 +1,45 @@
1
+ import * as React from "react";
2
+
3
+ import { cn } from "@silicajs/ui/lib/utils";
4
+
5
+ export type TocListItem = {
6
+ id: string;
7
+ label: React.ReactNode;
8
+ href: string;
9
+ depth: number;
10
+ };
11
+
12
+ export type TocListProps = {
13
+ items: TocListItem[];
14
+ activeId?: string;
15
+ className?: string;
16
+ };
17
+
18
+ export function TocList({ items, activeId, className }: TocListProps) {
19
+ if (items.length === 0) return null;
20
+ return (
21
+ <ol data-slot="toc-list" className={cn("flex flex-col gap-1 text-sm", className)}>
22
+ {items.map((item) => {
23
+ const isActive = activeId === item.id;
24
+ return (
25
+ <li
26
+ key={item.id}
27
+ style={{ paddingInlineStart: `${item.depth * 0.75}rem` }}
28
+ >
29
+ <a
30
+ href={item.href}
31
+ className={cn(
32
+ "block rounded-md px-2 py-1 transition-colors",
33
+ isActive
34
+ ? "font-medium text-primary"
35
+ : "text-muted-foreground hover:text-foreground"
36
+ )}
37
+ >
38
+ {item.label}
39
+ </a>
40
+ </li>
41
+ );
42
+ })}
43
+ </ol>
44
+ );
45
+ }
@@ -0,0 +1,66 @@
1
+ "use client"
2
+
3
+ import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip"
4
+
5
+ import { cn } from "@silicajs/ui/lib/utils"
6
+
7
+ function TooltipProvider({
8
+ delay = 0,
9
+ ...props
10
+ }: TooltipPrimitive.Provider.Props) {
11
+ return (
12
+ <TooltipPrimitive.Provider
13
+ data-slot="tooltip-provider"
14
+ delay={delay}
15
+ {...props}
16
+ />
17
+ )
18
+ }
19
+
20
+ function Tooltip({ ...props }: TooltipPrimitive.Root.Props) {
21
+ return <TooltipPrimitive.Root data-slot="tooltip" {...props} />
22
+ }
23
+
24
+ function TooltipTrigger({ ...props }: TooltipPrimitive.Trigger.Props) {
25
+ return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
26
+ }
27
+
28
+ function TooltipContent({
29
+ className,
30
+ side = "top",
31
+ sideOffset = 4,
32
+ align = "center",
33
+ alignOffset = 0,
34
+ children,
35
+ ...props
36
+ }: TooltipPrimitive.Popup.Props &
37
+ Pick<
38
+ TooltipPrimitive.Positioner.Props,
39
+ "align" | "alignOffset" | "side" | "sideOffset"
40
+ >) {
41
+ return (
42
+ <TooltipPrimitive.Portal>
43
+ <TooltipPrimitive.Positioner
44
+ align={align}
45
+ alignOffset={alignOffset}
46
+ side={side}
47
+ sideOffset={sideOffset}
48
+ className="isolate z-50"
49
+ >
50
+ <TooltipPrimitive.Popup
51
+ data-slot="tooltip-content"
52
+ className={cn(
53
+ "z-50 inline-flex w-fit max-w-xs origin-(--transform-origin) items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
54
+ className
55
+ )}
56
+ {...props}
57
+ >
58
+ {children}
59
+ <TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground data-[side=bottom]:top-1 data-[side=inline-end]:top-1/2! data-[side=inline-end]:-left-1 data-[side=inline-end]:-translate-y-1/2 data-[side=inline-start]:top-1/2! data-[side=inline-start]:-right-1 data-[side=inline-start]:-translate-y-1/2 data-[side=left]:top-1/2! data-[side=left]:-right-1 data-[side=left]:-translate-y-1/2 data-[side=right]:top-1/2! data-[side=right]:-left-1 data-[side=right]:-translate-y-1/2 data-[side=top]:-bottom-2.5" />
60
+ </TooltipPrimitive.Popup>
61
+ </TooltipPrimitive.Positioner>
62
+ </TooltipPrimitive.Portal>
63
+ )
64
+ }
65
+
66
+ export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
@@ -0,0 +1,19 @@
1
+ import * as React from "react"
2
+
3
+ const MOBILE_BREAKPOINT = 768
4
+
5
+ export function useIsMobile() {
6
+ const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
7
+
8
+ React.useEffect(() => {
9
+ const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
10
+ const onChange = () => {
11
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
12
+ }
13
+ mql.addEventListener("change", onChange)
14
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
15
+ return () => mql.removeEventListener("change", onChange)
16
+ }, [])
17
+
18
+ return !!isMobile
19
+ }
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from "clsx"
2
+ import { twMerge } from "tailwind-merge"
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs))
6
+ }
@@ -0,0 +1,168 @@
1
+ @import "tailwindcss";
2
+ @import "tw-animate-css";
3
+ @import "shadcn/tailwind.css";
4
+
5
+ @custom-variant dark (&:is(.dark *));
6
+ @source "../**/*.{ts,tsx}";
7
+
8
+ @theme inline {
9
+ --font-heading: var(--font-sans);
10
+ --color-sidebar-ring: var(--sidebar-ring);
11
+ --color-sidebar-border: var(--sidebar-border);
12
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
13
+ --color-sidebar-accent: var(--sidebar-accent);
14
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
15
+ --color-sidebar-primary: var(--sidebar-primary);
16
+ --color-sidebar-foreground: var(--sidebar-foreground);
17
+ --color-sidebar: var(--sidebar);
18
+ --color-chart-5: var(--chart-5);
19
+ --color-chart-4: var(--chart-4);
20
+ --color-chart-3: var(--chart-3);
21
+ --color-chart-2: var(--chart-2);
22
+ --color-chart-1: var(--chart-1);
23
+ --color-ring: var(--ring);
24
+ --color-input: var(--input);
25
+ --color-border: var(--border);
26
+ --color-destructive: var(--destructive);
27
+ --color-accent-foreground: var(--accent-foreground);
28
+ --color-accent: var(--accent);
29
+ --color-muted-foreground: var(--muted-foreground);
30
+ --color-muted: var(--muted);
31
+ --color-secondary-foreground: var(--secondary-foreground);
32
+ --color-secondary: var(--secondary);
33
+ --color-primary-foreground: var(--primary-foreground);
34
+ --color-primary: var(--primary);
35
+ --color-popover-foreground: var(--popover-foreground);
36
+ --color-popover: var(--popover);
37
+ --color-card-foreground: var(--card-foreground);
38
+ --color-card: var(--card);
39
+ --color-foreground: var(--foreground);
40
+ --color-background: var(--background);
41
+ --radius-sm: calc(var(--radius) * 0.6);
42
+ --radius-md: calc(var(--radius) * 0.8);
43
+ --radius-lg: var(--radius);
44
+ --radius-xl: calc(var(--radius) * 1.4);
45
+ --radius-2xl: calc(var(--radius) * 1.8);
46
+ --radius-3xl: calc(var(--radius) * 2.2);
47
+ --radius-4xl: calc(var(--radius) * 2.6);
48
+ --font-sans: var(--font-sans);
49
+ }
50
+
51
+ :root {
52
+ color-scheme: light;
53
+ --background: oklch(1 0 0);
54
+ --foreground: oklch(0.145 0 0);
55
+ --card: oklch(1 0 0);
56
+ --card-foreground: oklch(0.145 0 0);
57
+ --popover: oklch(1 0 0);
58
+ --popover-foreground: oklch(0.145 0 0);
59
+ --primary: oklch(0.205 0 0);
60
+ --primary-foreground: oklch(0.985 0 0);
61
+ --secondary: oklch(0.97 0 0);
62
+ --secondary-foreground: oklch(0.205 0 0);
63
+ --muted: oklch(0.97 0 0);
64
+ --muted-foreground: oklch(0.556 0 0);
65
+ --accent: oklch(0.97 0 0);
66
+ --accent-foreground: oklch(0.205 0 0);
67
+ --destructive: oklch(0.577 0.245 27.325);
68
+ --border: oklch(0.922 0 0);
69
+ --input: oklch(0.922 0 0);
70
+ --ring: oklch(0.708 0 0);
71
+ --chart-1: oklch(0.87 0 0);
72
+ --chart-2: oklch(0.556 0 0);
73
+ --chart-3: oklch(0.439 0 0);
74
+ --chart-4: oklch(0.371 0 0);
75
+ --chart-5: oklch(0.269 0 0);
76
+ --radius: 0.625rem;
77
+ --sidebar: oklch(0.985 0 0);
78
+ --sidebar-foreground: oklch(0.145 0 0);
79
+ --sidebar-primary: oklch(0.205 0 0);
80
+ --sidebar-primary-foreground: oklch(0.985 0 0);
81
+ --sidebar-accent: oklch(0.97 0 0);
82
+ --sidebar-accent-foreground: oklch(0.205 0 0);
83
+ --sidebar-border: oklch(0.922 0 0);
84
+ --sidebar-ring: oklch(0.708 0 0);
85
+ }
86
+
87
+ .dark {
88
+ color-scheme: dark;
89
+ --background: oklch(0.145 0 0);
90
+ --foreground: oklch(0.985 0 0);
91
+ --card: oklch(0.205 0 0);
92
+ --card-foreground: oklch(0.985 0 0);
93
+ --popover: oklch(0.205 0 0);
94
+ --popover-foreground: oklch(0.985 0 0);
95
+ --primary: oklch(0.922 0 0);
96
+ --primary-foreground: oklch(0.205 0 0);
97
+ --secondary: oklch(0.269 0 0);
98
+ --secondary-foreground: oklch(0.985 0 0);
99
+ --muted: oklch(0.269 0 0);
100
+ --muted-foreground: oklch(0.708 0 0);
101
+ --accent: oklch(0.269 0 0);
102
+ --accent-foreground: oklch(0.985 0 0);
103
+ --destructive: oklch(0.704 0.191 22.216);
104
+ --border: oklch(1 0 0 / 10%);
105
+ --input: oklch(1 0 0 / 15%);
106
+ --ring: oklch(0.556 0 0);
107
+ --chart-1: oklch(0.87 0 0);
108
+ --chart-2: oklch(0.556 0 0);
109
+ --chart-3: oklch(0.439 0 0);
110
+ --chart-4: oklch(0.371 0 0);
111
+ --chart-5: oklch(0.269 0 0);
112
+ --sidebar: oklch(0.205 0 0);
113
+ --sidebar-foreground: oklch(0.985 0 0);
114
+ --sidebar-primary: oklch(0.488 0.243 264.376);
115
+ --sidebar-primary-foreground: oklch(0.985 0 0);
116
+ --sidebar-accent: oklch(0.269 0 0);
117
+ --sidebar-accent-foreground: oklch(0.985 0 0);
118
+ --sidebar-border: oklch(1 0 0 / 10%);
119
+ --sidebar-ring: oklch(0.556 0 0);
120
+ }
121
+
122
+ @layer base {
123
+ * {
124
+ @apply border-border outline-ring/50;
125
+ }
126
+ body {
127
+ @apply bg-background text-foreground;
128
+ }
129
+ button:not(:disabled),
130
+ [role="button"]:not([aria-disabled="true"]) {
131
+ cursor: pointer;
132
+ }
133
+ }
134
+
135
+ .silica-heading-link {
136
+ color: inherit;
137
+ font-weight: inherit;
138
+ text-decoration: none;
139
+ }
140
+
141
+ :where(h1, h2, h3, h4, h5, h6)[id] {
142
+ scroll-margin-top: 5rem;
143
+ }
144
+
145
+ .silica-heading-link-icon {
146
+ display: inline-block;
147
+ width: 0.75em;
148
+ height: 0.75em;
149
+ margin-left: 0.35em;
150
+ vertical-align: -0.05em;
151
+ color: var(--muted-foreground);
152
+ opacity: 0;
153
+ transition:
154
+ opacity 150ms ease,
155
+ color 150ms ease;
156
+ }
157
+
158
+ .silica-heading-link:hover .silica-heading-link-icon,
159
+ .silica-heading-link:focus-visible .silica-heading-link-icon {
160
+ opacity: 1;
161
+ color: var(--primary);
162
+ }
163
+
164
+ .silica-heading-link:focus-visible {
165
+ border-radius: calc(var(--radius) / 2);
166
+ outline: 2px solid var(--ring);
167
+ outline-offset: 0.125em;
168
+ }