@vrugd/ui 0.1.1
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.
- package/dist/index.d.ts +59 -0
- package/dist/index.js +184 -0
- package/package.json +25 -0
- package/src/components/Button.tsx +36 -0
- package/src/components/Card.tsx +23 -0
- package/src/components/Container.tsx +23 -0
- package/src/components/Glass.tsx +30 -0
- package/src/components/Hero.tsx +46 -0
- package/src/components/Navbar.tsx +46 -0
- package/src/components/Section.tsx +26 -0
- package/src/components/Text.tsx +34 -0
- package/src/index.ts +10 -0
- package/src/styles/globals.css +18 -0
- package/src/styles/tokens.css +35 -0
- package/src/utils/cn.ts +3 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
declare function cn(...parts: Array<string | false | null | undefined>): string;
|
|
4
|
+
|
|
5
|
+
type ContainerProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
6
|
+
size?: "sm" | "md" | "lg" | "xl";
|
|
7
|
+
};
|
|
8
|
+
declare function Container({ size, className, ...props }: ContainerProps): React.JSX.Element;
|
|
9
|
+
|
|
10
|
+
type TextProps = React.HTMLAttributes<HTMLElement> & {
|
|
11
|
+
as?: "p" | "span" | "div";
|
|
12
|
+
tone?: "default" | "muted";
|
|
13
|
+
size?: "sm" | "md" | "lg";
|
|
14
|
+
};
|
|
15
|
+
declare function Text({ as, tone, size, className, ...props }: TextProps): React.JSX.Element;
|
|
16
|
+
|
|
17
|
+
type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
18
|
+
variant?: "solid" | "soft" | "ghost";
|
|
19
|
+
size?: "sm" | "md" | "lg";
|
|
20
|
+
};
|
|
21
|
+
declare function Button({ variant, size, className, ...props }: ButtonProps): React.JSX.Element;
|
|
22
|
+
|
|
23
|
+
type CardProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
24
|
+
tone?: "default" | "elevated";
|
|
25
|
+
};
|
|
26
|
+
declare function Card({ tone, className, ...props }: CardProps): React.JSX.Element;
|
|
27
|
+
|
|
28
|
+
type GlassProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
29
|
+
blur?: "sm" | "md" | "lg";
|
|
30
|
+
};
|
|
31
|
+
declare function Glass({ blur, className, ...props }: GlassProps): React.JSX.Element;
|
|
32
|
+
|
|
33
|
+
type NavItem = {
|
|
34
|
+
label: string;
|
|
35
|
+
href: string;
|
|
36
|
+
};
|
|
37
|
+
type NavbarProps = {
|
|
38
|
+
brand?: React.ReactNode;
|
|
39
|
+
items?: NavItem[];
|
|
40
|
+
right?: React.ReactNode;
|
|
41
|
+
className?: string;
|
|
42
|
+
};
|
|
43
|
+
declare function Navbar({ brand, items, right, className }: NavbarProps): React.JSX.Element;
|
|
44
|
+
|
|
45
|
+
type HeroProps = {
|
|
46
|
+
eyebrow?: string;
|
|
47
|
+
title: string;
|
|
48
|
+
description?: string;
|
|
49
|
+
actions?: React.ReactNode;
|
|
50
|
+
className?: string;
|
|
51
|
+
};
|
|
52
|
+
declare function Hero({ eyebrow, title, description, actions, className }: HeroProps): React.JSX.Element;
|
|
53
|
+
|
|
54
|
+
type SectionProps = React.HTMLAttributes<HTMLElement> & {
|
|
55
|
+
size?: "md" | "lg" | "xl";
|
|
56
|
+
};
|
|
57
|
+
declare function Section({ size, className, children, ...props }: SectionProps): React.JSX.Element;
|
|
58
|
+
|
|
59
|
+
export { Button, type ButtonProps, Card, type CardProps, Container, type ContainerProps, Glass, type GlassProps, Hero, type HeroProps, type NavItem, Navbar, type NavbarProps, Section, type SectionProps, Text, type TextProps, cn };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
// src/utils/cn.ts
|
|
2
|
+
function cn(...parts) {
|
|
3
|
+
return parts.filter(Boolean).join(" ");
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
// src/components/Container.tsx
|
|
7
|
+
import { jsx } from "react/jsx-runtime";
|
|
8
|
+
var sizeMap = {
|
|
9
|
+
sm: "max-w-[860px]",
|
|
10
|
+
md: "max-w-[1040px]",
|
|
11
|
+
lg: "max-w-[1200px]",
|
|
12
|
+
xl: "max-w-[1380px]"
|
|
13
|
+
};
|
|
14
|
+
function Container({
|
|
15
|
+
size = "lg",
|
|
16
|
+
className,
|
|
17
|
+
...props
|
|
18
|
+
}) {
|
|
19
|
+
return /* @__PURE__ */ jsx("div", { className: cn("mx-auto w-full px-5 sm:px-8", sizeMap[size], className), ...props });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// src/components/Text.tsx
|
|
23
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
24
|
+
var sizeCls = {
|
|
25
|
+
sm: "text-[13px] leading-[1.65]",
|
|
26
|
+
md: "text-[15px] leading-[1.75]",
|
|
27
|
+
lg: "text-[17px] leading-[1.75]"
|
|
28
|
+
};
|
|
29
|
+
function Text({
|
|
30
|
+
as = "p",
|
|
31
|
+
tone = "default",
|
|
32
|
+
size = "md",
|
|
33
|
+
className,
|
|
34
|
+
...props
|
|
35
|
+
}) {
|
|
36
|
+
const Comp = as;
|
|
37
|
+
return /* @__PURE__ */ jsx2(
|
|
38
|
+
Comp,
|
|
39
|
+
{
|
|
40
|
+
className: cn(
|
|
41
|
+
sizeCls[size],
|
|
42
|
+
tone === "muted" ? "text-[rgb(var(--muted))]" : "text-[rgb(var(--fg))]",
|
|
43
|
+
className
|
|
44
|
+
),
|
|
45
|
+
...props
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// src/components/Button.tsx
|
|
51
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
52
|
+
var sizeCls2 = {
|
|
53
|
+
sm: "h-9 px-3 text-[13px]",
|
|
54
|
+
md: "h-10 px-4 text-[14px]",
|
|
55
|
+
lg: "h-12 px-5 text-[15px]"
|
|
56
|
+
};
|
|
57
|
+
function Button({
|
|
58
|
+
variant = "solid",
|
|
59
|
+
size = "md",
|
|
60
|
+
className,
|
|
61
|
+
...props
|
|
62
|
+
}) {
|
|
63
|
+
const base = "uikit-focus inline-flex items-center justify-center gap-2 rounded-[var(--r-sm)] font-semibold transition-[transform,background-color,box-shadow,opacity] duration-[var(--dur-2)] ease-[var(--ease)] active:scale-[0.98]";
|
|
64
|
+
const solid = "bg-[rgb(var(--accent))] text-white shadow-[var(--shadow-sm)] hover:shadow-[var(--shadow-md)]";
|
|
65
|
+
const soft = "bg-[rgba(var(--accent),0.12)] text-[rgb(var(--fg))] hover:bg-[rgba(var(--accent),0.18)]";
|
|
66
|
+
const ghost = "bg-transparent text-[rgb(var(--fg))] hover:bg-[rgba(0,0,0,0.06)] dark:hover:bg-[rgba(255,255,255,0.08)]";
|
|
67
|
+
const v = variant === "solid" ? solid : variant === "soft" ? soft : ghost;
|
|
68
|
+
return /* @__PURE__ */ jsx3("button", { className: cn(base, sizeCls2[size], v, className), ...props });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// src/components/Card.tsx
|
|
72
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
73
|
+
function Card({
|
|
74
|
+
tone = "default",
|
|
75
|
+
className,
|
|
76
|
+
...props
|
|
77
|
+
}) {
|
|
78
|
+
return /* @__PURE__ */ jsx4(
|
|
79
|
+
"div",
|
|
80
|
+
{
|
|
81
|
+
className: cn(
|
|
82
|
+
"rounded-[var(--r-lg)] border border-[rgb(var(--border))] bg-[rgb(var(--surface))]",
|
|
83
|
+
tone === "elevated" ? "shadow-[var(--shadow-md)]" : "shadow-[var(--shadow-sm)]",
|
|
84
|
+
className
|
|
85
|
+
),
|
|
86
|
+
...props
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// src/components/Glass.tsx
|
|
92
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
93
|
+
var blurCls = {
|
|
94
|
+
sm: "backdrop-blur-[10px]",
|
|
95
|
+
md: "backdrop-blur-[16px]",
|
|
96
|
+
lg: "backdrop-blur-[22px]"
|
|
97
|
+
};
|
|
98
|
+
function Glass({
|
|
99
|
+
blur = "md",
|
|
100
|
+
className,
|
|
101
|
+
...props
|
|
102
|
+
}) {
|
|
103
|
+
return /* @__PURE__ */ jsx5(
|
|
104
|
+
"div",
|
|
105
|
+
{
|
|
106
|
+
className: cn(
|
|
107
|
+
"rounded-[var(--r-lg)] border",
|
|
108
|
+
"bg-[var(--glass)] border-[var(--glass-border)] shadow-[var(--shadow-sm)]",
|
|
109
|
+
blurCls[blur],
|
|
110
|
+
className
|
|
111
|
+
),
|
|
112
|
+
...props
|
|
113
|
+
}
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// src/components/Navbar.tsx
|
|
118
|
+
import { jsx as jsx6, jsxs } from "react/jsx-runtime";
|
|
119
|
+
function Navbar({
|
|
120
|
+
brand = /* @__PURE__ */ jsx6("span", { className: "font-semibold", children: "Brand" }),
|
|
121
|
+
items = [],
|
|
122
|
+
right,
|
|
123
|
+
className
|
|
124
|
+
}) {
|
|
125
|
+
return /* @__PURE__ */ jsx6("div", { className: cn("sticky top-0 z-50 py-3", className), children: /* @__PURE__ */ jsx6(Container, { size: "xl", children: /* @__PURE__ */ jsx6(Glass, { className: "px-4 py-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4", children: [
|
|
126
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-6", children: [
|
|
127
|
+
/* @__PURE__ */ jsx6("div", { className: "flex items-center gap-2", children: brand }),
|
|
128
|
+
/* @__PURE__ */ jsx6("nav", { className: "hidden md:flex items-center gap-4 text-[13px] text-[rgb(var(--muted))]", children: items.map((it) => /* @__PURE__ */ jsx6(
|
|
129
|
+
"a",
|
|
130
|
+
{
|
|
131
|
+
href: it.href,
|
|
132
|
+
className: "uikit-focus rounded-[var(--r-sm)] px-2 py-1 hover:text-[rgb(var(--fg))] transition-colors",
|
|
133
|
+
children: it.label
|
|
134
|
+
},
|
|
135
|
+
it.href
|
|
136
|
+
)) })
|
|
137
|
+
] }),
|
|
138
|
+
/* @__PURE__ */ jsx6("div", { className: "flex items-center gap-2", children: right })
|
|
139
|
+
] }) }) }) });
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// src/components/Hero.tsx
|
|
143
|
+
import { jsx as jsx7, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
144
|
+
function Hero({
|
|
145
|
+
eyebrow,
|
|
146
|
+
title,
|
|
147
|
+
description,
|
|
148
|
+
actions,
|
|
149
|
+
className
|
|
150
|
+
}) {
|
|
151
|
+
return /* @__PURE__ */ jsx7("section", { className: cn("py-14 sm:py-20", className), children: /* @__PURE__ */ jsx7(Container, { size: "xl", children: /* @__PURE__ */ jsxs2("div", { className: "max-w-[840px]", children: [
|
|
152
|
+
eyebrow ? /* @__PURE__ */ jsx7("div", { className: "text-[12px] tracking-[0.18em] uppercase text-[rgb(var(--muted))]", children: eyebrow }) : null,
|
|
153
|
+
/* @__PURE__ */ jsx7("h1", { className: "mt-3 text-[40px] leading-[1.06] sm:text-[56px] font-[750] tracking-[-0.02em]", children: title }),
|
|
154
|
+
description ? /* @__PURE__ */ jsx7(Text, { className: "mt-4 max-w-[680px]", tone: "muted", size: "lg", children: description }) : null,
|
|
155
|
+
actions ? /* @__PURE__ */ jsx7("div", { className: "mt-7 flex flex-wrap items-center gap-3", children: actions }) : null
|
|
156
|
+
] }) }) });
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// src/components/Section.tsx
|
|
160
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
161
|
+
var sizeMap2 = {
|
|
162
|
+
md: "py-10 sm:py-14",
|
|
163
|
+
lg: "py-14 sm:py-20",
|
|
164
|
+
xl: "py-16 sm:py-24"
|
|
165
|
+
};
|
|
166
|
+
function Section({
|
|
167
|
+
size = "lg",
|
|
168
|
+
className,
|
|
169
|
+
children,
|
|
170
|
+
...props
|
|
171
|
+
}) {
|
|
172
|
+
return /* @__PURE__ */ jsx8("section", { className: cn(sizeMap2[size], className), ...props, children: /* @__PURE__ */ jsx8(Container, { size: "xl", children }) });
|
|
173
|
+
}
|
|
174
|
+
export {
|
|
175
|
+
Button,
|
|
176
|
+
Card,
|
|
177
|
+
Container,
|
|
178
|
+
Glass,
|
|
179
|
+
Hero,
|
|
180
|
+
Navbar,
|
|
181
|
+
Section,
|
|
182
|
+
Text,
|
|
183
|
+
cn
|
|
184
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vrugd/ui",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"sideEffects": ["**/*.css"],
|
|
9
|
+
"files": ["dist", "src"],
|
|
10
|
+
"peerDependencies": {
|
|
11
|
+
"react": "^18.2.0",
|
|
12
|
+
"react-dom": "^18.2.0"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@types/node": "^25.0.10",
|
|
16
|
+
"@types/react": "^19.2.9",
|
|
17
|
+
"@types/react-dom": "^19.2.3",
|
|
18
|
+
"tsup": "^8.5.1",
|
|
19
|
+
"typescript": "^5.5.0"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
23
|
+
"prepublishOnly": "bun run build"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "../utils/cn";
|
|
3
|
+
|
|
4
|
+
export type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
5
|
+
variant?: "solid" | "soft" | "ghost";
|
|
6
|
+
size?: "sm" | "md" | "lg";
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const sizeCls: Record<NonNullable<ButtonProps["size"]>, string> = {
|
|
10
|
+
sm: "h-9 px-3 text-[13px]",
|
|
11
|
+
md: "h-10 px-4 text-[14px]",
|
|
12
|
+
lg: "h-12 px-5 text-[15px]"
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export function Button({
|
|
16
|
+
variant = "solid",
|
|
17
|
+
size = "md",
|
|
18
|
+
className,
|
|
19
|
+
...props
|
|
20
|
+
}: ButtonProps): React.JSX.Element {
|
|
21
|
+
const base =
|
|
22
|
+
"uikit-focus inline-flex items-center justify-center gap-2 rounded-[var(--r-sm)] font-semibold " +
|
|
23
|
+
"transition-[transform,background-color,box-shadow,opacity] duration-[var(--dur-2)] ease-[var(--ease)] " +
|
|
24
|
+
"active:scale-[0.98]";
|
|
25
|
+
|
|
26
|
+
const solid =
|
|
27
|
+
"bg-[rgb(var(--accent))] text-white shadow-[var(--shadow-sm)] hover:shadow-[var(--shadow-md)]";
|
|
28
|
+
const soft =
|
|
29
|
+
"bg-[rgba(var(--accent),0.12)] text-[rgb(var(--fg))] hover:bg-[rgba(var(--accent),0.18)]";
|
|
30
|
+
const ghost =
|
|
31
|
+
"bg-transparent text-[rgb(var(--fg))] hover:bg-[rgba(0,0,0,0.06)] dark:hover:bg-[rgba(255,255,255,0.08)]";
|
|
32
|
+
|
|
33
|
+
const v = variant === "solid" ? solid : variant === "soft" ? soft : ghost;
|
|
34
|
+
|
|
35
|
+
return <button className={cn(base, sizeCls[size], v, className)} {...props} />;
|
|
36
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "../utils/cn";
|
|
3
|
+
|
|
4
|
+
export type CardProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
5
|
+
tone?: "default" | "elevated";
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export function Card({
|
|
9
|
+
tone = "default",
|
|
10
|
+
className,
|
|
11
|
+
...props
|
|
12
|
+
}: CardProps): React.JSX.Element {
|
|
13
|
+
return (
|
|
14
|
+
<div
|
|
15
|
+
className={cn(
|
|
16
|
+
"rounded-[var(--r-lg)] border border-[rgb(var(--border))] bg-[rgb(var(--surface))]",
|
|
17
|
+
tone === "elevated" ? "shadow-[var(--shadow-md)]" : "shadow-[var(--shadow-sm)]",
|
|
18
|
+
className
|
|
19
|
+
)}
|
|
20
|
+
{...props}
|
|
21
|
+
/>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "../utils/cn";
|
|
3
|
+
|
|
4
|
+
export type ContainerProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
5
|
+
size?: "sm" | "md" | "lg" | "xl";
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const sizeMap: Record<NonNullable<ContainerProps["size"]>, string> = {
|
|
9
|
+
sm: "max-w-[860px]",
|
|
10
|
+
md: "max-w-[1040px]",
|
|
11
|
+
lg: "max-w-[1200px]",
|
|
12
|
+
xl: "max-w-[1380px]"
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export function Container({
|
|
16
|
+
size = "lg",
|
|
17
|
+
className,
|
|
18
|
+
...props
|
|
19
|
+
}: ContainerProps): React.JSX.Element {
|
|
20
|
+
return (
|
|
21
|
+
<div className={cn("mx-auto w-full px-5 sm:px-8", sizeMap[size], className)} {...props} />
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "../utils/cn";
|
|
3
|
+
|
|
4
|
+
export type GlassProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
5
|
+
blur?: "sm" | "md" | "lg";
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const blurCls: Record<NonNullable<GlassProps["blur"]>, string> = {
|
|
9
|
+
sm: "backdrop-blur-[10px]",
|
|
10
|
+
md: "backdrop-blur-[16px]",
|
|
11
|
+
lg: "backdrop-blur-[22px]"
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function Glass({
|
|
15
|
+
blur = "md",
|
|
16
|
+
className,
|
|
17
|
+
...props
|
|
18
|
+
}: GlassProps): React.JSX.Element {
|
|
19
|
+
return (
|
|
20
|
+
<div
|
|
21
|
+
className={cn(
|
|
22
|
+
"rounded-[var(--r-lg)] border",
|
|
23
|
+
"bg-[var(--glass)] border-[var(--glass-border)] shadow-[var(--shadow-sm)]",
|
|
24
|
+
blurCls[blur],
|
|
25
|
+
className
|
|
26
|
+
)}
|
|
27
|
+
{...props}
|
|
28
|
+
/>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Container } from "./Container";
|
|
3
|
+
import { Text } from "./Text";
|
|
4
|
+
import { cn } from "../utils/cn";
|
|
5
|
+
|
|
6
|
+
export type HeroProps = {
|
|
7
|
+
eyebrow?: string;
|
|
8
|
+
title: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
actions?: React.ReactNode;
|
|
11
|
+
className?: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function Hero({
|
|
15
|
+
eyebrow,
|
|
16
|
+
title,
|
|
17
|
+
description,
|
|
18
|
+
actions,
|
|
19
|
+
className
|
|
20
|
+
}: HeroProps): React.JSX.Element {
|
|
21
|
+
return (
|
|
22
|
+
<section className={cn("py-14 sm:py-20", className)}>
|
|
23
|
+
<Container size="xl">
|
|
24
|
+
<div className="max-w-[840px]">
|
|
25
|
+
{eyebrow ? (
|
|
26
|
+
<div className="text-[12px] tracking-[0.18em] uppercase text-[rgb(var(--muted))]">
|
|
27
|
+
{eyebrow}
|
|
28
|
+
</div>
|
|
29
|
+
) : null}
|
|
30
|
+
|
|
31
|
+
<h1 className="mt-3 text-[40px] leading-[1.06] sm:text-[56px] font-[750] tracking-[-0.02em]">
|
|
32
|
+
{title}
|
|
33
|
+
</h1>
|
|
34
|
+
|
|
35
|
+
{description ? (
|
|
36
|
+
<Text className="mt-4 max-w-[680px]" tone="muted" size="lg">
|
|
37
|
+
{description}
|
|
38
|
+
</Text>
|
|
39
|
+
) : null}
|
|
40
|
+
|
|
41
|
+
{actions ? <div className="mt-7 flex flex-wrap items-center gap-3">{actions}</div> : null}
|
|
42
|
+
</div>
|
|
43
|
+
</Container>
|
|
44
|
+
</section>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "../utils/cn";
|
|
3
|
+
import { Container } from "./Container";
|
|
4
|
+
import { Glass } from "./Glass";
|
|
5
|
+
|
|
6
|
+
export type NavItem = { label: string; href: string };
|
|
7
|
+
|
|
8
|
+
export type NavbarProps = {
|
|
9
|
+
brand?: React.ReactNode;
|
|
10
|
+
items?: NavItem[];
|
|
11
|
+
right?: React.ReactNode;
|
|
12
|
+
className?: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export function Navbar({
|
|
16
|
+
brand = <span className="font-semibold">Brand</span>,
|
|
17
|
+
items = [],
|
|
18
|
+
right,
|
|
19
|
+
className
|
|
20
|
+
}: NavbarProps): React.JSX.Element {
|
|
21
|
+
return (
|
|
22
|
+
<div className={cn("sticky top-0 z-50 py-3", className)}>
|
|
23
|
+
<Container size="xl">
|
|
24
|
+
<Glass className="px-4 py-3">
|
|
25
|
+
<div className="flex items-center justify-between gap-4">
|
|
26
|
+
<div className="flex items-center gap-6">
|
|
27
|
+
<div className="flex items-center gap-2">{brand}</div>
|
|
28
|
+
<nav className="hidden md:flex items-center gap-4 text-[13px] text-[rgb(var(--muted))]">
|
|
29
|
+
{items.map((it) => (
|
|
30
|
+
<a
|
|
31
|
+
key={it.href}
|
|
32
|
+
href={it.href}
|
|
33
|
+
className="uikit-focus rounded-[var(--r-sm)] px-2 py-1 hover:text-[rgb(var(--fg))] transition-colors"
|
|
34
|
+
>
|
|
35
|
+
{it.label}
|
|
36
|
+
</a>
|
|
37
|
+
))}
|
|
38
|
+
</nav>
|
|
39
|
+
</div>
|
|
40
|
+
<div className="flex items-center gap-2">{right}</div>
|
|
41
|
+
</div>
|
|
42
|
+
</Glass>
|
|
43
|
+
</Container>
|
|
44
|
+
</div>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "../utils/cn";
|
|
3
|
+
import { Container } from "./Container";
|
|
4
|
+
|
|
5
|
+
export type SectionProps = React.HTMLAttributes<HTMLElement> & {
|
|
6
|
+
size?: "md" | "lg" | "xl";
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const sizeMap: Record<NonNullable<SectionProps["size"]>, string> = {
|
|
10
|
+
md: "py-10 sm:py-14",
|
|
11
|
+
lg: "py-14 sm:py-20",
|
|
12
|
+
xl: "py-16 sm:py-24"
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export function Section({
|
|
16
|
+
size = "lg",
|
|
17
|
+
className,
|
|
18
|
+
children,
|
|
19
|
+
...props
|
|
20
|
+
}: SectionProps): React.JSX.Element {
|
|
21
|
+
return (
|
|
22
|
+
<section className={cn(sizeMap[size], className)} {...props}>
|
|
23
|
+
<Container size="xl">{children}</Container>
|
|
24
|
+
</section>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "../utils/cn";
|
|
3
|
+
|
|
4
|
+
export type TextProps = React.HTMLAttributes<HTMLElement> & {
|
|
5
|
+
as?: "p" | "span" | "div";
|
|
6
|
+
tone?: "default" | "muted";
|
|
7
|
+
size?: "sm" | "md" | "lg";
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const sizeCls: Record<NonNullable<TextProps["size"]>, string> = {
|
|
11
|
+
sm: "text-[13px] leading-[1.65]",
|
|
12
|
+
md: "text-[15px] leading-[1.75]",
|
|
13
|
+
lg: "text-[17px] leading-[1.75]"
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function Text({
|
|
17
|
+
as = "p",
|
|
18
|
+
tone = "default",
|
|
19
|
+
size = "md",
|
|
20
|
+
className,
|
|
21
|
+
...props
|
|
22
|
+
}: TextProps): React.JSX.Element {
|
|
23
|
+
const Comp = as;
|
|
24
|
+
return (
|
|
25
|
+
<Comp
|
|
26
|
+
className={cn(
|
|
27
|
+
sizeCls[size],
|
|
28
|
+
tone === "muted" ? "text-[rgb(var(--muted))]" : "text-[rgb(var(--fg))]",
|
|
29
|
+
className
|
|
30
|
+
)}
|
|
31
|
+
{...props}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from "./utils/cn";
|
|
2
|
+
|
|
3
|
+
export * from "./components/Container";
|
|
4
|
+
export * from "./components/Text";
|
|
5
|
+
export * from "./components/Button";
|
|
6
|
+
export * from "./components/Card";
|
|
7
|
+
export * from "./components/Glass";
|
|
8
|
+
export * from "./components/Navbar";
|
|
9
|
+
export * from "./components/Hero";
|
|
10
|
+
export * from "./components/Section";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
@import "./tokens.css";
|
|
2
|
+
|
|
3
|
+
* { box-sizing: border-box; }
|
|
4
|
+
html, body { height: 100%; }
|
|
5
|
+
|
|
6
|
+
body {
|
|
7
|
+
margin: 0;
|
|
8
|
+
color: rgb(var(--fg));
|
|
9
|
+
background: rgb(var(--bg));
|
|
10
|
+
-webkit-font-smoothing: antialiased;
|
|
11
|
+
text-rendering: optimizeLegibility;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.uikit-focus:focus-visible {
|
|
15
|
+
outline: 3px solid rgba(var(--accent), 0.35);
|
|
16
|
+
outline-offset: 2px;
|
|
17
|
+
border-radius: var(--r-sm);
|
|
18
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--bg: 255 255 255;
|
|
3
|
+
--fg: 17 17 17;
|
|
4
|
+
--muted: 113 113 122;
|
|
5
|
+
|
|
6
|
+
--surface: 255 255 255;
|
|
7
|
+
--border: 228 228 231;
|
|
8
|
+
|
|
9
|
+
--accent: 10 132 255;
|
|
10
|
+
|
|
11
|
+
--r-sm: 12px;
|
|
12
|
+
--r-md: 16px;
|
|
13
|
+
--r-lg: 22px;
|
|
14
|
+
|
|
15
|
+
--shadow-sm: 0 1px 0 rgba(0,0,0,0.06), 0 8px 24px rgba(0,0,0,0.06);
|
|
16
|
+
--shadow-md: 0 1px 0 rgba(0,0,0,0.06), 0 16px 40px rgba(0,0,0,0.10);
|
|
17
|
+
|
|
18
|
+
--glass: rgba(255, 255, 255, 0.70);
|
|
19
|
+
--glass-border: rgba(255, 255, 255, 0.55);
|
|
20
|
+
|
|
21
|
+
--ease: cubic-bezier(0.2, 0.8, 0.2, 1);
|
|
22
|
+
--dur-2: 240ms;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
[data-theme="dark"] {
|
|
26
|
+
--bg: 10 10 11;
|
|
27
|
+
--fg: 245 245 246;
|
|
28
|
+
--muted: 161 161 170;
|
|
29
|
+
|
|
30
|
+
--surface: 18 18 20;
|
|
31
|
+
--border: 39 39 42;
|
|
32
|
+
|
|
33
|
+
--glass: rgba(20, 20, 22, 0.60);
|
|
34
|
+
--glass-border: rgba(255, 255, 255, 0.10);
|
|
35
|
+
}
|
package/src/utils/cn.ts
ADDED