chore: scaffold phase 0 workspace
This commit is contained in:
@@ -0,0 +1,8 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
storybook-static
|
||||||
|
coverage
|
||||||
|
.turbo
|
||||||
|
.pnpm-store
|
||||||
|
.storybook
|
||||||
|
.DS_Store
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "@ai-ui/docs",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"build-storybook": "storybook build",
|
||||||
|
"storybook": "storybook dev -p 6006 --ci",
|
||||||
|
"storybook:smoke": "storybook dev -p 6006 --ci --smoke-test",
|
||||||
|
"typecheck": "tsc --noEmit -p tsconfig.json"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@ai-ui/tokens": "workspace:*",
|
||||||
|
"@ai-ui/ui": "workspace:*",
|
||||||
|
"react": "^18.3.1",
|
||||||
|
"react-dom": "^18.3.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
import { cn, motionDurations } from "@ai-ui/ui";
|
||||||
|
import { themeNames } from "@ai-ui/tokens";
|
||||||
|
import type { Meta, StoryObj } from "@storybook/react";
|
||||||
|
|
||||||
|
function FoundationShowcase() {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-[var(--color-background)] px-6 py-10 text-[var(--color-foreground)] sm:px-10">
|
||||||
|
<div className="mx-auto flex w-full max-w-5xl flex-col gap-6">
|
||||||
|
<div className="max-w-3xl space-y-3">
|
||||||
|
<p className="text-sm uppercase tracking-[0.28em] text-[var(--color-muted-foreground)]">
|
||||||
|
AI UI / Phase 0
|
||||||
|
</p>
|
||||||
|
<h1 className="text-4xl font-semibold tracking-tight sm:text-5xl">
|
||||||
|
Monorepo scaffolding for a source-owned component system.
|
||||||
|
</h1>
|
||||||
|
<p className="max-w-2xl text-base leading-7 text-[var(--color-muted-foreground)] sm:text-lg">
|
||||||
|
The repo now has workspace packages for tokens, UI utilities, and docs.
|
||||||
|
The next phase can focus on component contracts instead of repo setup.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid gap-4 md:grid-cols-[minmax(0,2fr)_minmax(0,1fr)]">
|
||||||
|
<section className="rounded-[var(--radius-lg)] border border-[var(--color-border)] bg-[var(--color-card)] p-6 shadow-[var(--shadow-sm)]">
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex items-center justify-between gap-4">
|
||||||
|
<h2 className="text-xl font-semibold">Workspace packages</h2>
|
||||||
|
<span className="rounded-full bg-[var(--color-primary)] px-3 py-1 text-xs font-medium text-[var(--color-primary-foreground)]">
|
||||||
|
ready
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="grid gap-3 sm:grid-cols-3">
|
||||||
|
{["packages/tokens", "packages/ui", "apps/docs"].map((item) => (
|
||||||
|
<div
|
||||||
|
key={item}
|
||||||
|
className={cn(
|
||||||
|
"rounded-[var(--radius-md)] border border-[var(--color-border)] bg-[var(--color-surface)] px-4 py-3 text-sm",
|
||||||
|
"transition-transform duration-200 ease-[var(--ease-standard)] hover:-translate-y-[2px]"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{item}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<aside className="rounded-[var(--radius-lg)] border border-[var(--color-border)] bg-[var(--color-surface)] p-6 shadow-[var(--shadow-xs)]">
|
||||||
|
<div className="space-y-3">
|
||||||
|
<h2 className="text-xl font-semibold">Theme baseline</h2>
|
||||||
|
<ul className="space-y-2 text-sm text-[var(--color-muted-foreground)]">
|
||||||
|
{themeNames.map((themeName) => (
|
||||||
|
<li key={themeName} className="flex items-center justify-between gap-4">
|
||||||
|
<span>{themeName}</span>
|
||||||
|
<span className="font-medium text-[var(--color-foreground)]">enabled</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
<div className="rounded-[var(--radius-md)] bg-[var(--color-background)] px-4 py-3 text-xs text-[var(--color-muted-foreground)]">
|
||||||
|
<span className="font-medium text-[var(--color-foreground)]">Motion base:</span>{" "}
|
||||||
|
{motionDurations.base}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const meta = {
|
||||||
|
title: "Foundation/Phase 0",
|
||||||
|
component: FoundationShowcase
|
||||||
|
} satisfies Meta<typeof FoundationShowcase>;
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
|
||||||
|
type Story = StoryObj<typeof meta>;
|
||||||
|
|
||||||
|
export const Default: Story = {};
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
@import "tailwindcss";
|
||||||
|
@import "@ai-ui/tokens/styles.css";
|
||||||
|
|
||||||
|
:root {
|
||||||
|
background: var(--color-background);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"types": ["node"]
|
||||||
|
},
|
||||||
|
"include": [".storybook/**/*", "src/**/*"]
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import js from "@eslint/js";
|
||||||
|
import globals from "globals";
|
||||||
|
import reactHooks from "eslint-plugin-react-hooks";
|
||||||
|
import reactRefresh from "eslint-plugin-react-refresh";
|
||||||
|
import tseslint from "typescript-eslint";
|
||||||
|
|
||||||
|
export default tseslint.config(
|
||||||
|
{
|
||||||
|
ignores: ["dist/**", "node_modules/**", "storybook-static/**", "coverage/**"]
|
||||||
|
},
|
||||||
|
js.configs.recommended,
|
||||||
|
...tseslint.configs.recommended,
|
||||||
|
{
|
||||||
|
files: ["**/*.{ts,tsx,mts,cts}"],
|
||||||
|
languageOptions: {
|
||||||
|
ecmaVersion: "latest",
|
||||||
|
sourceType: "module",
|
||||||
|
globals: {
|
||||||
|
...globals.browser,
|
||||||
|
...globals.node
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
"react-hooks": reactHooks,
|
||||||
|
"react-refresh": reactRefresh
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
...reactHooks.configs.recommended.rules,
|
||||||
|
"react-refresh/only-export-components": [
|
||||||
|
"warn",
|
||||||
|
{
|
||||||
|
allowConstantExport: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"name": "ai-ui",
|
||||||
|
"private": true,
|
||||||
|
"packageManager": "pnpm@10.25.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=24.0.0",
|
||||||
|
"pnpm": ">=10.0.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "pnpm --filter @ai-ui/tokens build && pnpm --filter @ai-ui/ui build",
|
||||||
|
"build:docs": "pnpm --filter @ai-ui/docs build-storybook",
|
||||||
|
"dev:docs": "pnpm --filter @ai-ui/docs storybook",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"typecheck": "pnpm -r typecheck"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.39.4",
|
||||||
|
"@storybook/addon-a11y": "^8.6.14",
|
||||||
|
"@storybook/addon-essentials": "^8.6.14",
|
||||||
|
"@storybook/addon-interactions": "^8.6.14",
|
||||||
|
"@storybook/react": "^8.6.14",
|
||||||
|
"@storybook/react-vite": "^8.6.14",
|
||||||
|
"@tailwindcss/vite": "^4.2.2",
|
||||||
|
"@types/node": "^25.5.0",
|
||||||
|
"@types/react": "^18.3.28",
|
||||||
|
"@types/react-dom": "^18.3.7",
|
||||||
|
"eslint": "^9.39.4",
|
||||||
|
"eslint-plugin-react-hooks": "^7.0.1",
|
||||||
|
"eslint-plugin-react-refresh": "^0.5.2",
|
||||||
|
"globals": "^17.4.0",
|
||||||
|
"storybook": "^8.6.14",
|
||||||
|
"tailwindcss": "^4.2.2",
|
||||||
|
"tsup": "^8.5.1",
|
||||||
|
"typescript": "^5.9.3",
|
||||||
|
"typescript-eslint": "^8.57.1",
|
||||||
|
"vite": "^6.4.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"name": "@ai-ui/tokens",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"sideEffects": [
|
||||||
|
"**/*.css"
|
||||||
|
],
|
||||||
|
"exports": {
|
||||||
|
".": "./src/index.ts",
|
||||||
|
"./base.css": "./src/base.css",
|
||||||
|
"./motion.css": "./src/motion.css",
|
||||||
|
"./styles.css": "./src/styles.css",
|
||||||
|
"./tokens.css": "./src/tokens.css"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist",
|
||||||
|
"src"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsup src/index.ts --clean --dts --format esm",
|
||||||
|
"typecheck": "tsc --noEmit -p tsconfig.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
color-scheme: light;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
min-height: 100vh;
|
||||||
|
background: var(--color-background);
|
||||||
|
color: var(--color-foreground);
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
textarea,
|
||||||
|
select {
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
export const themeNames = ["light", "dark"] as const;
|
||||||
|
|
||||||
|
export const motionScale = {
|
||||||
|
fast: "var(--dur-fast)",
|
||||||
|
base: "var(--dur-base)",
|
||||||
|
slow: "var(--dur-slow)"
|
||||||
|
} as const;
|
||||||
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
:root {
|
||||||
|
--dur-fast: 120ms;
|
||||||
|
--dur-base: 200ms;
|
||||||
|
--dur-slow: 320ms;
|
||||||
|
|
||||||
|
--ease-standard: cubic-bezier(0.22, 1, 0.36, 1);
|
||||||
|
--ease-emphasized: cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
|
|
||||||
|
--distance-xs: 4px;
|
||||||
|
--distance-sm: 8px;
|
||||||
|
--distance-md: 16px;
|
||||||
|
|
||||||
|
--scale-press: 0.98;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
:root {
|
||||||
|
--dur-fast: 1ms;
|
||||||
|
--dur-base: 1ms;
|
||||||
|
--dur-slow: 1ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
animation-duration: 1ms !important;
|
||||||
|
animation-iteration-count: 1 !important;
|
||||||
|
scroll-behavior: auto !important;
|
||||||
|
transition-duration: 1ms !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
@import "./base.css";
|
||||||
|
@import "./tokens.css";
|
||||||
|
@import "./motion.css";
|
||||||
|
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
:root,
|
||||||
|
[data-theme="light"] {
|
||||||
|
color-scheme: light;
|
||||||
|
--font-sans: "Avenir Next", "Segoe UI", sans-serif;
|
||||||
|
--font-mono: "SF Mono", "SFMono-Regular", "Consolas", monospace;
|
||||||
|
|
||||||
|
--color-background: oklch(0.985 0.004 85);
|
||||||
|
--color-foreground: oklch(0.24 0.03 60);
|
||||||
|
--color-surface: oklch(0.965 0.008 80);
|
||||||
|
--color-surface-strong: oklch(0.93 0.012 78);
|
||||||
|
--color-border: oklch(0.87 0.01 75);
|
||||||
|
--color-ring: oklch(0.56 0.12 32);
|
||||||
|
--color-primary: oklch(0.53 0.15 30);
|
||||||
|
--color-primary-foreground: oklch(0.98 0.01 80);
|
||||||
|
--color-muted: oklch(0.94 0.008 78);
|
||||||
|
--color-muted-foreground: oklch(0.42 0.028 60);
|
||||||
|
--color-accent: oklch(0.76 0.1 82);
|
||||||
|
--color-card: color-mix(in oklch, var(--color-surface) 86%, white 14%);
|
||||||
|
--color-card-foreground: var(--color-foreground);
|
||||||
|
|
||||||
|
--radius-sm: 10px;
|
||||||
|
--radius-md: 16px;
|
||||||
|
--radius-lg: 24px;
|
||||||
|
|
||||||
|
--shadow-xs: 0 1px 2px oklch(0.28 0.02 55 / 0.06);
|
||||||
|
--shadow-sm: 0 8px 24px oklch(0.28 0.02 55 / 0.08);
|
||||||
|
--shadow-md: 0 18px 48px oklch(0.28 0.03 55 / 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] {
|
||||||
|
color-scheme: dark;
|
||||||
|
--color-background: oklch(0.2 0.015 60);
|
||||||
|
--color-foreground: oklch(0.94 0.01 80);
|
||||||
|
--color-surface: oklch(0.26 0.018 60);
|
||||||
|
--color-surface-strong: oklch(0.31 0.018 60);
|
||||||
|
--color-border: oklch(0.4 0.015 60);
|
||||||
|
--color-ring: oklch(0.7 0.12 35);
|
||||||
|
--color-primary: oklch(0.72 0.13 40);
|
||||||
|
--color-primary-foreground: oklch(0.22 0.014 60);
|
||||||
|
--color-muted: oklch(0.28 0.015 60);
|
||||||
|
--color-muted-foreground: oklch(0.8 0.02 72);
|
||||||
|
--color-accent: oklch(0.68 0.09 82);
|
||||||
|
--color-card: color-mix(in oklch, var(--color-surface) 90%, black 10%);
|
||||||
|
--color-card-foreground: var(--color-foreground);
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"types": ["node"]
|
||||||
|
},
|
||||||
|
"include": ["src/**/*"]
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"name": "@ai-ui/ui",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"exports": {
|
||||||
|
".": "./src/index.ts"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist",
|
||||||
|
"src"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsup src/index.ts --clean --dts --format esm,cjs",
|
||||||
|
"typecheck": "tsc --noEmit -p tsconfig.json"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@ai-ui/tokens": "workspace:*",
|
||||||
|
"class-variance-authority": "^0.7.1",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
|
"tailwind-merge": "^3.5.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"react": "^18.3.1",
|
||||||
|
"react-dom": "^18.3.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^18.3.1 || ^19.0.0",
|
||||||
|
"react-dom": "^18.3.1 || ^19.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export { cn } from "./lib/cn";
|
||||||
|
export { cva, cx, type VariantProps } from "./lib/cva";
|
||||||
|
export { motionDurations, motionEasings } from "./lib/motion";
|
||||||
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { clsx, type ClassValue } from "clsx";
|
||||||
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
|
export function cn(...inputs: ClassValue[]) {
|
||||||
|
return twMerge(clsx(inputs));
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export { cva, cx, type VariantProps } from "class-variance-authority";
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
export const motionDurations = {
|
||||||
|
fast: "var(--dur-fast)",
|
||||||
|
base: "var(--dur-base)",
|
||||||
|
slow: "var(--dur-slow)"
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const motionEasings = {
|
||||||
|
standard: "var(--ease-standard)",
|
||||||
|
emphasized: "var(--ease-emphasized)"
|
||||||
|
} as const;
|
||||||
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"types": ["node"]
|
||||||
|
},
|
||||||
|
"include": ["src/**/*"]
|
||||||
|
}
|
||||||
|
|
||||||
Generated
+4466
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,4 @@
|
|||||||
|
packages:
|
||||||
|
- "apps/*"
|
||||||
|
- "packages/*"
|
||||||
|
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"lib": ["ES2023", "DOM", "DOM.Iterable"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Bundler",
|
||||||
|
"allowArbitraryExtensions": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@ai-ui/ui": ["packages/ui/src/index.ts"],
|
||||||
|
"@ai-ui/tokens": ["packages/tokens/src/index.ts"],
|
||||||
|
"@ai-ui/tokens/*": ["packages/tokens/src/*"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"types": ["node"]
|
||||||
|
},
|
||||||
|
"include": []
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user