@ntrs/core 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,39 @@
1
+ import * as react from 'react';
2
+ import { ButtonHTMLAttributes, ReactNode } from 'react';
3
+ import { ClassValue } from 'clsx';
4
+
5
+ type ButtonVariant = 'default' | 'outline' | 'destructive' | 'ghost';
6
+ type ButtonSize = 'sm' | 'md' | 'lg';
7
+ interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
8
+ variant?: ButtonVariant;
9
+ size?: ButtonSize;
10
+ }
11
+ declare function Button({ variant, size, className, ...props }: ButtonProps): react.JSX.Element;
12
+
13
+ interface StatPillProps {
14
+ label: string;
15
+ value: string | number;
16
+ className?: string;
17
+ }
18
+ declare function StatPill({ label, value, className }: StatPillProps): react.JSX.Element;
19
+
20
+ interface PageHeaderProps {
21
+ title: string;
22
+ subtitle?: string;
23
+ action?: ReactNode;
24
+ className?: string;
25
+ }
26
+ declare function PageHeader({ title, subtitle, action, className }: PageHeaderProps): react.JSX.Element;
27
+
28
+ type NavItem = 'arena' | 'upload' | 'leaderboard' | 'my-memes';
29
+ interface AppShellProps {
30
+ active: NavItem;
31
+ userName?: string;
32
+ onNavigate?: (item: NavItem) => void;
33
+ children: ReactNode;
34
+ }
35
+ declare function AppShell({ active, userName, onNavigate, children }: AppShellProps): react.JSX.Element;
36
+
37
+ declare function cn(...inputs: ClassValue[]): string;
38
+
39
+ export { AppShell, type AppShellProps, Button, type ButtonProps, type ButtonSize, type ButtonVariant, type NavItem, PageHeader, type PageHeaderProps, StatPill, type StatPillProps, cn };
package/dist/index.js ADDED
@@ -0,0 +1,108 @@
1
+ "use client";
2
+
3
+ // src/lib/cn.ts
4
+ import { clsx } from "clsx";
5
+ import { twMerge } from "tailwind-merge";
6
+ function cn(...inputs) {
7
+ return twMerge(clsx(inputs));
8
+ }
9
+
10
+ // src/button/index.tsx
11
+ import { jsx } from "react/jsx-runtime";
12
+ var variantClasses = {
13
+ default: "bg-primary text-primary-foreground hover:opacity-90",
14
+ outline: "border border-border bg-transparent text-foreground hover:bg-muted",
15
+ destructive: "bg-destructive text-destructive-foreground hover:opacity-90",
16
+ ghost: "bg-transparent text-foreground hover:bg-muted"
17
+ };
18
+ var sizeClasses = {
19
+ sm: "h-8 px-3 text-sm",
20
+ md: "h-10 px-4 text-sm",
21
+ lg: "h-12 px-6 text-base"
22
+ };
23
+ function Button({ variant = "default", size = "md", className, ...props }) {
24
+ return /* @__PURE__ */ jsx(
25
+ "button",
26
+ {
27
+ className: cn(
28
+ "inline-flex items-center justify-center gap-2 rounded-lg font-medium transition-colors",
29
+ "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary",
30
+ "disabled:pointer-events-none disabled:opacity-50",
31
+ variantClasses[variant],
32
+ sizeClasses[size],
33
+ className
34
+ ),
35
+ ...props
36
+ }
37
+ );
38
+ }
39
+
40
+ // src/stat-pill/index.tsx
41
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
42
+ function StatPill({ label, value, className }) {
43
+ return /* @__PURE__ */ jsxs("span", { className: cn("inline-flex items-center gap-2 rounded-full bg-muted px-3 py-1 text-sm", className), children: [
44
+ /* @__PURE__ */ jsx2("span", { className: "text-muted-foreground", children: label }),
45
+ /* @__PURE__ */ jsx2("span", { className: "font-semibold tabular-nums", children: value })
46
+ ] });
47
+ }
48
+
49
+ // src/page-header/index.tsx
50
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
51
+ function PageHeader({ title, subtitle, action, className }) {
52
+ return /* @__PURE__ */ jsxs2("div", { className: cn("mb-8 flex flex-wrap items-end justify-between gap-4", className), children: [
53
+ /* @__PURE__ */ jsxs2("div", { children: [
54
+ /* @__PURE__ */ jsx3("h1", { className: "text-2xl font-bold tracking-tight", children: title }),
55
+ subtitle && /* @__PURE__ */ jsx3("p", { className: "mt-1 text-muted-foreground", children: subtitle })
56
+ ] }),
57
+ action
58
+ ] });
59
+ }
60
+
61
+ // src/app-shell/index.tsx
62
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
63
+ var NAV = [
64
+ { id: "arena", label: "Arena", icon: "\u2694\uFE0F" },
65
+ { id: "upload", label: "Upload", icon: "\u{1F4E4}" },
66
+ { id: "leaderboard", label: "Leaderboard", icon: "\u{1F3C6}" },
67
+ { id: "my-memes", label: "My Memes", icon: "\u{1F5C2}\uFE0F" }
68
+ ];
69
+ function AppShell({ active, userName, onNavigate, children }) {
70
+ return /* @__PURE__ */ jsxs3("div", { className: "color-background min-h-screen", children: [
71
+ /* @__PURE__ */ jsx4("header", { className: "color-card sticky top-0 z-10 border-b border-border", children: /* @__PURE__ */ jsxs3("div", { className: "mx-auto flex h-16 max-w-5xl items-center justify-between gap-6 px-4", children: [
72
+ /* @__PURE__ */ jsxs3("span", { className: "text-lg font-black tracking-tight", children: [
73
+ "\u2694\uFE0F Meme Battle ",
74
+ /* @__PURE__ */ jsx4("span", { className: "text-primary", children: "Arena" })
75
+ ] }),
76
+ /* @__PURE__ */ jsx4("nav", { "aria-label": "Main", className: "flex items-center gap-1", children: NAV.map((item) => /* @__PURE__ */ jsxs3(
77
+ "button",
78
+ {
79
+ type: "button",
80
+ onClick: () => onNavigate?.(item.id),
81
+ "aria-current": active === item.id ? "page" : void 0,
82
+ className: cn(
83
+ "rounded-lg px-3 py-2 text-sm font-medium transition-colors",
84
+ active === item.id ? "bg-primary/10 text-primary" : "text-muted-foreground hover:bg-muted hover:text-foreground"
85
+ ),
86
+ children: [
87
+ /* @__PURE__ */ jsx4("span", { "aria-hidden": true, className: "mr-1.5", children: item.icon }),
88
+ item.label
89
+ ]
90
+ },
91
+ item.id
92
+ )) }),
93
+ userName && /* @__PURE__ */ jsxs3("span", { className: "hidden items-center gap-2 text-sm text-muted-foreground sm:flex", children: [
94
+ /* @__PURE__ */ jsx4("span", { className: "flex h-8 w-8 items-center justify-center rounded-full bg-primary/15 font-bold text-primary", children: userName[0].toUpperCase() }),
95
+ userName
96
+ ] })
97
+ ] }) }),
98
+ /* @__PURE__ */ jsx4("main", { className: "mx-auto max-w-5xl px-4 py-8", children })
99
+ ] });
100
+ }
101
+ export {
102
+ AppShell,
103
+ Button,
104
+ PageHeader,
105
+ StatPill,
106
+ cn
107
+ };
108
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/cn.ts","../src/button/index.tsx","../src/stat-pill/index.tsx","../src/page-header/index.tsx","../src/app-shell/index.tsx"],"sourcesContent":["import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","'use client';\n\nimport type { ButtonHTMLAttributes } from 'react';\nimport { cn } from '../lib/cn';\n\nexport type ButtonVariant = 'default' | 'outline' | 'destructive' | 'ghost';\nexport type ButtonSize = 'sm' | 'md' | 'lg';\n\nexport interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: ButtonVariant;\n size?: ButtonSize;\n}\n\nconst variantClasses: Record<ButtonVariant, string> = {\n default: 'bg-primary text-primary-foreground hover:opacity-90',\n outline: 'border border-border bg-transparent text-foreground hover:bg-muted',\n destructive: 'bg-destructive text-destructive-foreground hover:opacity-90',\n ghost: 'bg-transparent text-foreground hover:bg-muted',\n};\n\nconst sizeClasses: Record<ButtonSize, string> = {\n sm: 'h-8 px-3 text-sm',\n md: 'h-10 px-4 text-sm',\n lg: 'h-12 px-6 text-base',\n};\n\nexport function Button({ variant = 'default', size = 'md', className, ...props }: ButtonProps) {\n return (\n <button\n className={cn(\n 'inline-flex items-center justify-center gap-2 rounded-lg font-medium transition-colors',\n 'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary',\n 'disabled:pointer-events-none disabled:opacity-50',\n variantClasses[variant],\n sizeClasses[size],\n className,\n )}\n {...props}\n />\n );\n}\n","import { cn } from '../lib/cn';\n\nexport interface StatPillProps {\n label: string;\n value: string | number;\n className?: string;\n}\n\nexport function StatPill({ label, value, className }: StatPillProps) {\n return (\n <span className={cn('inline-flex items-center gap-2 rounded-full bg-muted px-3 py-1 text-sm', className)}>\n <span className=\"text-muted-foreground\">{label}</span>\n <span className=\"font-semibold tabular-nums\">{value}</span>\n </span>\n );\n}\n","import type { ReactNode } from 'react';\nimport { cn } from '../lib/cn';\n\nexport interface PageHeaderProps {\n title: string;\n subtitle?: string;\n action?: ReactNode;\n className?: string;\n}\n\nexport function PageHeader({ title, subtitle, action, className }: PageHeaderProps) {\n return (\n <div className={cn('mb-8 flex flex-wrap items-end justify-between gap-4', className)}>\n <div>\n <h1 className=\"text-2xl font-bold tracking-tight\">{title}</h1>\n {subtitle && <p className=\"mt-1 text-muted-foreground\">{subtitle}</p>}\n </div>\n {action}\n </div>\n );\n}\n","'use client';\n\nimport type { ReactNode } from 'react';\nimport { cn } from '../lib/cn';\n\nexport type NavItem = 'arena' | 'upload' | 'leaderboard' | 'my-memes';\n\nexport interface AppShellProps {\n active: NavItem;\n userName?: string;\n onNavigate?: (item: NavItem) => void;\n children: ReactNode;\n}\n\nconst NAV: { id: NavItem; label: string; icon: string }[] = [\n { id: 'arena', label: 'Arena', icon: 'โš”๏ธ' },\n { id: 'upload', label: 'Upload', icon: '๐Ÿ“ค' },\n { id: 'leaderboard', label: 'Leaderboard', icon: '๐Ÿ†' },\n { id: 'my-memes', label: 'My Memes', icon: '๐Ÿ—‚๏ธ' },\n];\n\nexport function AppShell({ active, userName, onNavigate, children }: AppShellProps) {\n return (\n <div className=\"color-background min-h-screen\">\n <header className=\"color-card sticky top-0 z-10 border-b border-border\">\n <div className=\"mx-auto flex h-16 max-w-5xl items-center justify-between gap-6 px-4\">\n <span className=\"text-lg font-black tracking-tight\">\n โš”๏ธ Meme Battle <span className=\"text-primary\">Arena</span>\n </span>\n <nav aria-label=\"Main\" className=\"flex items-center gap-1\">\n {NAV.map((item) => (\n <button\n key={item.id}\n type=\"button\"\n onClick={() => onNavigate?.(item.id)}\n aria-current={active === item.id ? 'page' : undefined}\n className={cn(\n 'rounded-lg px-3 py-2 text-sm font-medium transition-colors',\n active === item.id\n ? 'bg-primary/10 text-primary'\n : 'text-muted-foreground hover:bg-muted hover:text-foreground',\n )}\n >\n <span aria-hidden className=\"mr-1.5\">{item.icon}</span>\n {item.label}\n </button>\n ))}\n </nav>\n {userName && (\n <span className=\"hidden items-center gap-2 text-sm text-muted-foreground sm:flex\">\n <span className=\"flex h-8 w-8 items-center justify-center rounded-full bg-primary/15 font-bold text-primary\">\n {userName[0].toUpperCase()}\n </span>\n {userName}\n </span>\n )}\n </div>\n </header>\n <main className=\"mx-auto max-w-5xl px-4 py-8\">{children}</main>\n </div>\n );\n}\n"],"mappings":"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","names":["jsx","jsx","jsxs","jsx","jsxs"]}
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@ntrs/core",
3
+ "version": "0.1.0",
4
+ "description": "NTRS design system โ€” generic primitives and design tokens shared by every project",
5
+ "type": "module",
6
+ "sideEffects": ["**/*.css"],
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js"
11
+ },
12
+ "./tokens.css": "./src/tokens.css"
13
+ },
14
+ "files": ["dist", "src/tokens.css"],
15
+ "scripts": {
16
+ "build": "tsup",
17
+ "prepublishOnly": "npm run build"
18
+ },
19
+ "dependencies": {
20
+ "clsx": "^2.1.1",
21
+ "tailwind-merge": "^3.6.0"
22
+ },
23
+ "peerDependencies": {
24
+ "react": "^19.0.0",
25
+ "react-dom": "^19.0.0"
26
+ }
27
+ }
package/src/tokens.css ADDED
@@ -0,0 +1,76 @@
1
+ @import "tailwindcss";
2
+
3
+ /* class-based dark mode for Tailwind v4 (boilerplate ยง14) */
4
+ @variant dark (.dark &);
5
+
6
+ @layer base {
7
+ :root {
8
+ --background: 0 0% 100%;
9
+ --foreground: 0 0% 3.9%;
10
+ --card: 0 0% 100%;
11
+ --card-foreground: 0 0% 3.9%;
12
+ --primary: 262 83% 58%;
13
+ --primary-foreground: 0 0% 98%;
14
+ --muted: 0 0% 96.1%;
15
+ --muted-foreground: 0 0% 45.1%;
16
+ --border: 0 0% 89.8%;
17
+ --destructive: 0 84% 60%;
18
+ --destructive-foreground: 0 0% 98%;
19
+ --success: 142 71% 40%;
20
+ --success-foreground: 0 0% 98%;
21
+ --gold: 43 96% 50%;
22
+ --radius: 0.75rem;
23
+ }
24
+
25
+ .dark {
26
+ --background: 0 0% 5.9%;
27
+ --foreground: 0 0% 98%;
28
+ --card: 0 0% 9%;
29
+ --card-foreground: 0 0% 98%;
30
+ --primary: 262 83% 66%;
31
+ --primary-foreground: 0 0% 9%;
32
+ --muted: 0 0% 14.9%;
33
+ --muted-foreground: 0 0% 63.9%;
34
+ --border: 0 0% 16.9%;
35
+ --destructive: 0 62% 52%;
36
+ --destructive-foreground: 0 0% 98%;
37
+ --success: 142 60% 45%;
38
+ --success-foreground: 0 0% 98%;
39
+ --gold: 43 96% 56%;
40
+ }
41
+
42
+ /* smooth-transition surface utilities (boilerplate convention) */
43
+ .color-background {
44
+ background-color: hsl(var(--background));
45
+ transition: background-color 200ms ease-in-out;
46
+ }
47
+ .color-foreground {
48
+ color: hsl(var(--foreground));
49
+ transition: color 200ms ease-in-out;
50
+ }
51
+ .color-card {
52
+ background-color: hsl(var(--card));
53
+ color: hsl(var(--card-foreground));
54
+ transition: background-color 200ms ease-in-out, color 200ms ease-in-out;
55
+ }
56
+ }
57
+
58
+ /* expose tokens as Tailwind utilities: bg-background, text-muted-foreground, border-border, ... */
59
+ @theme inline {
60
+ --color-background: hsl(var(--background));
61
+ --color-foreground: hsl(var(--foreground));
62
+ --color-card: hsl(var(--card));
63
+ --color-card-foreground: hsl(var(--card-foreground));
64
+ --color-primary: hsl(var(--primary));
65
+ --color-primary-foreground: hsl(var(--primary-foreground));
66
+ --color-muted: hsl(var(--muted));
67
+ --color-muted-foreground: hsl(var(--muted-foreground));
68
+ --color-border: hsl(var(--border));
69
+ --color-destructive: hsl(var(--destructive));
70
+ --color-destructive-foreground: hsl(var(--destructive-foreground));
71
+ --color-success: hsl(var(--success));
72
+ --color-success-foreground: hsl(var(--success-foreground));
73
+ --color-gold: hsl(var(--gold));
74
+ --radius-lg: var(--radius);
75
+ --radius-xl: calc(var(--radius) + 0.25rem);
76
+ }