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