Initial Commit
This commit is contained in:
commit
9baf870446
35 changed files with 3318 additions and 0 deletions
8
.editorconfig
Normal file
8
.editorconfig
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
[*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue,css,scss,sass,less,styl}]
|
||||||
|
charset = utf-8
|
||||||
|
indent_size = 2
|
||||||
|
indent_style = space
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
end_of_line = lf
|
||||||
|
max_line_length = 100
|
||||||
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
* text=auto eol=lf
|
||||||
48
.gitignore
vendored
Normal file
48
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
.DS_Store
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
coverage
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Cypress
|
||||||
|
/cypress/videos/
|
||||||
|
/cypress/screenshots/
|
||||||
|
|
||||||
|
# Vitest
|
||||||
|
__screenshots__/
|
||||||
|
|
||||||
|
# Vite
|
||||||
|
*.timestamp-*-*.mjs
|
||||||
|
|
||||||
|
packages/*/dist
|
||||||
|
packages/*/node_modules
|
||||||
|
packages/*/.storybook-static
|
||||||
|
packages/*/storybook-static
|
||||||
|
|
||||||
|
# Storybook
|
||||||
|
.storybook/node_modules
|
||||||
|
node_modules/.cache
|
||||||
10
.oxlintrc.json
Normal file
10
.oxlintrc.json
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
||||||
|
"plugins": ["eslint", "typescript", "unicorn", "oxc", "vue", "vitest"],
|
||||||
|
"env": {
|
||||||
|
"browser": true
|
||||||
|
},
|
||||||
|
"categories": {
|
||||||
|
"correctness": "error"
|
||||||
|
}
|
||||||
|
}
|
||||||
6
.prettierrc.json
Normal file
6
.prettierrc.json
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/prettierrc",
|
||||||
|
"semi": false,
|
||||||
|
"singleQuote": true,
|
||||||
|
"printWidth": 100
|
||||||
|
}
|
||||||
10
.vscode/extensions.json
vendored
Normal file
10
.vscode/extensions.json
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
"Vue.volar",
|
||||||
|
"vitest.explorer",
|
||||||
|
"dbaeumer.vscode-eslint",
|
||||||
|
"EditorConfig.EditorConfig",
|
||||||
|
"oxc.oxc-vscode",
|
||||||
|
"esbenp.prettier-vscode"
|
||||||
|
]
|
||||||
|
}
|
||||||
133
README.md
Normal file
133
README.md
Normal file
|
|
@ -0,0 +1,133 @@
|
||||||
|
# nychthemeron
|
||||||
|
|
||||||
|
A CSS-reactive Vue component library built on PrimeVue (Unstyled), shipped with the nychthemeron theme as a default.
|
||||||
|
|
||||||
|
## About
|
||||||
|
|
||||||
|
nychthemeron provides a curated set of Vue 3 components powered by PrimeVue's unstyled components and the nychthemeron theme system. This approach gives you complete control over component styling while maintaining a consistent, polished design language out of the box.
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- **CSS-Reactive**: Components respond to CSS changes without requiring JavaScript updates
|
||||||
|
- **PrimeVue Powered**: Built on the robust, accessible PrimeVue unstyled component library
|
||||||
|
- **Theme Included**: Ships with the nychthemeron theme, ready to use immediately
|
||||||
|
- **TypeScript**: Full type support for Vue 3 and TypeScript projects
|
||||||
|
- **Customizable**: Override or extend the default theme to match your design system
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install @nychthemeron/library primevue vue@^3.5.0
|
||||||
|
```
|
||||||
|
|
||||||
|
### Basic Usage
|
||||||
|
|
||||||
|
Import the library components and styles:
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { NychButton } from '@nychthemeron/library'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NychButton label="Click me" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@import '@nychthemeron/library/style';
|
||||||
|
@import '@nychthemeron/library/theme';
|
||||||
|
</style>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Components
|
||||||
|
|
||||||
|
- **NychButton** - Customizable button component
|
||||||
|
|
||||||
|
More components coming soon.
|
||||||
|
|
||||||
|
## Customization
|
||||||
|
|
||||||
|
### Using the Default Theme
|
||||||
|
|
||||||
|
The nychthemeron theme is included by default:
|
||||||
|
|
||||||
|
```css
|
||||||
|
@import '@nychthemeron/library/theme';
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom Styling
|
||||||
|
|
||||||
|
Override or extend component styles by importing only the base styles and providing your own CSS:
|
||||||
|
|
||||||
|
```css
|
||||||
|
@import '@nychthemeron/library/style';
|
||||||
|
|
||||||
|
/* Your custom theme */
|
||||||
|
:root {
|
||||||
|
--primary-color: #your-color;
|
||||||
|
/* More CSS variables */
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
This is a monorepo containing:
|
||||||
|
|
||||||
|
- **`packages/library`** - The component library package
|
||||||
|
- **`packages/playground`** - Development environment and examples
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
### Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Development Server
|
||||||
|
|
||||||
|
Start the playground to develop and test components:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
Build the library for production:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
|
||||||
|
Run unit tests:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun test
|
||||||
|
```
|
||||||
|
|
||||||
|
### Linting and Formatting
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun lint # Check and fix linting issues
|
||||||
|
bun format # Format code with Prettier
|
||||||
|
bun type-check # Run TypeScript type checking
|
||||||
|
```
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- **Node.js**: >=20.19.0 or >=22.12.0
|
||||||
|
- **Vue**: ^3.5.0
|
||||||
|
- **PrimeVue**: ^4.5.0
|
||||||
|
|
||||||
|
## Browser Support
|
||||||
|
|
||||||
|
Supports all modern browsers with CSS Grid and CSS Variables support.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
708
docs/superpowers/plans/2026-05-11-library-with-playground.md
Normal file
708
docs/superpowers/plans/2026-05-11-library-with-playground.md
Normal file
|
|
@ -0,0 +1,708 @@
|
||||||
|
# Library with Playground Implementation Plan
|
||||||
|
|
||||||
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
|
||||||
|
**Goal:** Transform nychthemeron into a monorepo with a Vue component library (with PrimeVue + Tailwind) and interactive Storybook playground.
|
||||||
|
|
||||||
|
**Architecture:** Bun workspaces manage two independent packages — library (builds to ESM) and playground (Storybook dev server and static build). Shared configs at root level.
|
||||||
|
|
||||||
|
**Tech Stack:** Vue 3, PrimeVue, Tailwind CSS, Vite, Storybook, Vitest, TypeScript, Bun workspaces
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 1: Create monorepo directory structure
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `packages/library/` directory
|
||||||
|
- Create: `packages/playground/` directory
|
||||||
|
|
||||||
|
- [ ] **Step 1: Create packages directories**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p packages/library/src/{components,composables}
|
||||||
|
mkdir -p packages/library/__tests__
|
||||||
|
mkdir -p packages/playground/.storybook
|
||||||
|
mkdir -p packages/playground/stories
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Verify structure**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ls -la packages/
|
||||||
|
# Expected output shows: library/ and playground/ directories
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 2: Move existing source to library package
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Move: `src/` → `packages/library/src/`
|
||||||
|
- Delete: `vite.config.ts`, `vitest.config.ts`, `index.html` (will recreate in packages)
|
||||||
|
|
||||||
|
- [ ] **Step 1: Move src directory**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mv src packages/library/src
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Verify move**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ls packages/library/src/
|
||||||
|
# Expected: App.vue, main.ts, __tests__/
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: Delete old root-level config files**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rm vite.config.ts vitest.config.ts index.html
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 3: Update root package.json for monorepo
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `package.json`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Add workspaces and update root config**
|
||||||
|
|
||||||
|
Replace the entire `package.json` with:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "nychthemeron",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"workspaces": [
|
||||||
|
"packages/*"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"dev": "bun -r --cwd packages/playground dev",
|
||||||
|
"build": "bun -r --cwd packages/library build && bun -r --cwd packages/playground build",
|
||||||
|
"preview": "bun -r --cwd packages/playground preview",
|
||||||
|
"type-check": "vue-tsc --build",
|
||||||
|
"lint": "eslint packages/*/src --fix",
|
||||||
|
"format": "prettier --write --experimental-cli packages/*/src",
|
||||||
|
"test": "bun -r --cwd packages/library test:unit"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@tsconfig/node24": "^24.0.4",
|
||||||
|
"@types/jsdom": "^28.0.1",
|
||||||
|
"@types/node": "^24.12.2",
|
||||||
|
"@vitejs/plugin-vue": "^6.0.6",
|
||||||
|
"@vitest/eslint-plugin": "^1.6.16",
|
||||||
|
"@vue/eslint-config-typescript": "^14.7.0",
|
||||||
|
"@vue/test-utils": "^2.4.6",
|
||||||
|
"@vue/tsconfig": "^0.9.1",
|
||||||
|
"eslint": "^10.2.1",
|
||||||
|
"eslint-config-prettier": "^10.1.8",
|
||||||
|
"eslint-plugin-oxlint": "~1.60.0",
|
||||||
|
"eslint-plugin-vue": "~10.8.0",
|
||||||
|
"jiti": "^2.6.1",
|
||||||
|
"jsdom": "^29.0.2",
|
||||||
|
"npm-run-all2": "^8.0.4",
|
||||||
|
"oxlint": "~1.60.0",
|
||||||
|
"prettier": "3.8.3",
|
||||||
|
"typescript": "~6.0.0",
|
||||||
|
"vite": "^8.0.8",
|
||||||
|
"vite-plugin-vue-devtools": "^8.1.1",
|
||||||
|
"vitest": "^4.1.4",
|
||||||
|
"vue": "^3.5.32",
|
||||||
|
"vue-tsc": "^3.2.6"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Install dependencies for new workspace structure**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun install
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected output: Bun detects workspaces and creates symlinks in each `packages/*/node_modules`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 4: Create library package.json
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `packages/library/package.json`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Create library package config**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "@nychthemeron/library",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"type": "module",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"import": "./dist/index.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"build": "vite build",
|
||||||
|
"test:unit": "vitest"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": "^3.5.0",
|
||||||
|
"primevue": "^18.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitest/coverage-v8": "^2.0.0",
|
||||||
|
"@vue/test-utils": "^2.4.6"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Write this to `packages/library/package.json`
|
||||||
|
|
||||||
|
- [ ] **Step 2: Verify file exists**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat packages/library/package.json | grep '"name"'
|
||||||
|
# Expected: "@nychthemeron/library"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 5: Create library tsconfig.json
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `packages/library/tsconfig.json`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Create library TypeScript config**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "./dist",
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"emitDeclarationOnly": false
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*.ts",
|
||||||
|
"src/**/*.vue"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"dist",
|
||||||
|
"**/*.spec.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Write this to `packages/library/tsconfig.json`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 6: Create library vite.config.ts
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `packages/library/vite.config.ts`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Create Vite library config**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [vue()],
|
||||||
|
build: {
|
||||||
|
lib: {
|
||||||
|
entry: fileURLToPath(new URL('./src/index.ts', import.meta.url)),
|
||||||
|
name: 'NychthemeronLibrary',
|
||||||
|
formats: ['es'],
|
||||||
|
fileName: () => 'index.js',
|
||||||
|
},
|
||||||
|
rollupOptions: {
|
||||||
|
external: ['vue', 'primevue'],
|
||||||
|
output: {
|
||||||
|
globals: {
|
||||||
|
vue: 'Vue',
|
||||||
|
primevue: 'PrimeVue',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
Write this to `packages/library/vite.config.ts`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 7: Create library vitest.config.ts
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `packages/library/vitest.config.ts`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Create Vitest config**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { defineConfig } from 'vitest/config'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [vue()],
|
||||||
|
test: {
|
||||||
|
globals: true,
|
||||||
|
environment: 'jsdom',
|
||||||
|
include: ['**/*.spec.ts'],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
Write this to `packages/library/vitest.config.ts`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 8: Create library export structure
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `packages/library/src/components/index.ts`
|
||||||
|
- Create: `packages/library/src/composables/index.ts`
|
||||||
|
- Modify: `packages/library/src/index.ts`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Create components index (empty for now)**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Re-export all components here as they're added
|
||||||
|
// export { default as Button } from './Button/Button.vue'
|
||||||
|
// export { default as DatePicker } from './DatePicker/DatePicker.vue'
|
||||||
|
```
|
||||||
|
|
||||||
|
Write this to `packages/library/src/components/index.ts`
|
||||||
|
|
||||||
|
- [ ] **Step 2: Create composables index (empty for now)**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Re-export all composables here as they're added
|
||||||
|
// export { useDate } from './useDate'
|
||||||
|
```
|
||||||
|
|
||||||
|
Write this to `packages/library/src/composables/index.ts`
|
||||||
|
|
||||||
|
- [ ] **Step 3: Create root library entry point**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export * from './components'
|
||||||
|
export * from './composables'
|
||||||
|
```
|
||||||
|
|
||||||
|
Write this to `packages/library/src/index.ts`
|
||||||
|
|
||||||
|
- [ ] **Step 4: Remove old App.vue and main.ts from library (they were for the app)**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rm packages/library/src/App.vue packages/library/src/main.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 9: Create playground package.json
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `packages/playground/package.json`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Create playground package config**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "@nychthemeron/playground",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "storybook dev -p 6006",
|
||||||
|
"build": "storybook build",
|
||||||
|
"preview": "npx vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@nychthemeron/library": "workspace:*",
|
||||||
|
"primevue": "^18.0.0",
|
||||||
|
"vue": "^3.5.32"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@storybook/addon-essentials": "^8.0.0",
|
||||||
|
"@storybook/addon-interactions": "^8.0.0",
|
||||||
|
"@storybook/addon-links": "^8.0.0",
|
||||||
|
"@storybook/blocks": "^8.0.0",
|
||||||
|
"@storybook/vue3": "^8.0.0",
|
||||||
|
"@storybook/vue3-vite": "^8.0.0",
|
||||||
|
"storybook": "^8.0.0",
|
||||||
|
"tailwindcss": "^3.4.0",
|
||||||
|
"autoprefixer": "^10.4.0",
|
||||||
|
"postcss": "^8.4.0",
|
||||||
|
"typescript": "~6.0.0",
|
||||||
|
"vue": "^3.5.32"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Write this to `packages/playground/package.json`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 10: Create playground tsconfig.json
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `packages/playground/tsconfig.json`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Create playground TypeScript config**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": false,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"lib": [
|
||||||
|
"dom",
|
||||||
|
"dom.iterable",
|
||||||
|
"esnext"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
".storybook/**/*.ts",
|
||||||
|
".storybook/**/*.tsx",
|
||||||
|
".storybook/**/*.vue",
|
||||||
|
"stories/**/*.ts",
|
||||||
|
"stories/**/*.tsx",
|
||||||
|
"stories/**/*.vue"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Write this to `packages/playground/tsconfig.json`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 11: Create playground Tailwind config
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `packages/playground/tailwind.config.ts`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Create Tailwind config**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import type { Config } from 'tailwindcss'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
content: [
|
||||||
|
'./.storybook/**/*.{ts,tsx,vue}',
|
||||||
|
'./stories/**/*.{ts,tsx,vue}',
|
||||||
|
'../library/src/**/*.vue',
|
||||||
|
],
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
primary: {
|
||||||
|
50: '#f0f9ff',
|
||||||
|
500: '#0ea5e9',
|
||||||
|
900: '#0c2d6b',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
} satisfies Config
|
||||||
|
```
|
||||||
|
|
||||||
|
Write this to `packages/playground/tailwind.config.ts`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 12: Create Storybook main config
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `packages/playground/.storybook/main.ts`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Create Storybook main configuration**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import type { StorybookConfig } from '@storybook/vue3-vite'
|
||||||
|
|
||||||
|
const config: StorybookConfig = {
|
||||||
|
stories: [
|
||||||
|
'../stories/**/*.stories.ts',
|
||||||
|
],
|
||||||
|
addons: [
|
||||||
|
'@storybook/addon-essentials',
|
||||||
|
'@storybook/addon-interactions',
|
||||||
|
'@storybook/addon-links',
|
||||||
|
],
|
||||||
|
framework: {
|
||||||
|
name: '@storybook/vue3-vite',
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
core: {
|
||||||
|
disableTelemetry: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default config
|
||||||
|
```
|
||||||
|
|
||||||
|
Write this to `packages/playground/.storybook/main.ts`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 13: Create Storybook preview config
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `packages/playground/.storybook/preview.ts`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Create Storybook preview configuration**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import type { Preview } from '@storybook/vue3'
|
||||||
|
import 'primevue/resources/themes/lara-light-blue/theme.css'
|
||||||
|
import 'primevue/resources/primevue.min.css'
|
||||||
|
import '../tailwind.css'
|
||||||
|
|
||||||
|
const preview: Preview = {
|
||||||
|
parameters: {
|
||||||
|
layout: 'centered',
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
component: 'Component documentation',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default preview
|
||||||
|
```
|
||||||
|
|
||||||
|
Write this to `packages/playground/.storybook/preview.ts`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 14: Create Tailwind CSS entry point
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `packages/playground/tailwind.css`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Create Tailwind directives file**
|
||||||
|
|
||||||
|
```css
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
```
|
||||||
|
|
||||||
|
Write this to `packages/playground/tailwind.css`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 15: Create example story
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `packages/playground/stories/Example.stories.ts`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Create example story for testing**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import type { Meta, StoryObj } from '@storybook/vue3'
|
||||||
|
|
||||||
|
const meta = {
|
||||||
|
title: 'Example/Welcome',
|
||||||
|
parameters: {
|
||||||
|
layout: 'centered',
|
||||||
|
},
|
||||||
|
tags: ['autodocs'],
|
||||||
|
} satisfies Meta
|
||||||
|
|
||||||
|
export default meta
|
||||||
|
type Story = StoryObj<typeof meta>
|
||||||
|
|
||||||
|
export const Welcome: Story = {
|
||||||
|
render: () => ({
|
||||||
|
template: `
|
||||||
|
<div class="p-8 text-center">
|
||||||
|
<h1 class="text-3xl font-bold text-primary-500 mb-4">
|
||||||
|
Nychthemeron Component Library
|
||||||
|
</h1>
|
||||||
|
<p class="text-gray-600">
|
||||||
|
Interactive playground for Vue components with PrimeVue + Tailwind
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Write this to `packages/playground/stories/Example.stories.ts`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 16: Install dependencies and verify structure
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- None (verification only)
|
||||||
|
|
||||||
|
- [ ] **Step 1: Install dependencies for all packages**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun install
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: All packages get dependencies installed, workspace links created
|
||||||
|
|
||||||
|
- [ ] **Step 2: Verify workspace structure**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ls -la packages/library/node_modules/@nychthemeron/library
|
||||||
|
# Expected: symlink to ../..
|
||||||
|
|
||||||
|
ls -la packages/playground/node_modules/@nychthemeron/library
|
||||||
|
# Expected: symlink to ../../../library
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: Type-check across monorepo**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun type-check
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: No TypeScript errors
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 17: Test dev server startup
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- None (verification only)
|
||||||
|
|
||||||
|
- [ ] **Step 1: Start Storybook playground**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected output:
|
||||||
|
```
|
||||||
|
Storybook 8.x started
|
||||||
|
Local: http://localhost:6006
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Verify Storybook loads in browser**
|
||||||
|
|
||||||
|
Open `http://localhost:6006` in a browser.
|
||||||
|
|
||||||
|
Expected: Storybook UI loads with "Example/Welcome" story visible showing the welcome message.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Verify story renders correctly**
|
||||||
|
|
||||||
|
In Storybook, click "Example/Welcome" → verify the welcome message displays with correct styling.
|
||||||
|
|
||||||
|
Expected:
|
||||||
|
- Title: "Nychthemeron Component Library" (blue, large)
|
||||||
|
- Subtitle: "Interactive playground..." (gray, smaller)
|
||||||
|
|
||||||
|
- [ ] **Step 4: Stop dev server**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Press Ctrl+C in terminal
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 18: Test library build
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- None (verification only)
|
||||||
|
|
||||||
|
- [ ] **Step 1: Build library**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun -r --cwd packages/library build
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected output:
|
||||||
|
```
|
||||||
|
✓ 123 modules transformed
|
||||||
|
dist/index.js 1.23 kb
|
||||||
|
dist/index.d.ts 0.5 kb
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Verify dist files exist**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ls packages/library/dist/
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected:
|
||||||
|
```
|
||||||
|
index.d.ts
|
||||||
|
index.js
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: Verify exports are correct**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
head -20 packages/library/dist/index.js
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: JavaScript code with ES module exports (starts with `import` or defines exports)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 19: Initialize git for monorepo
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- None (git initialization only)
|
||||||
|
|
||||||
|
- [ ] **Step 1: Initialize git repo (if not already)**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git init
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Add gitignore entries for workspace**
|
||||||
|
|
||||||
|
Append to `.gitignore`:
|
||||||
|
|
||||||
|
```
|
||||||
|
packages/*/dist
|
||||||
|
packages/*/node_modules
|
||||||
|
packages/*/.storybook-static
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: Create initial commit (SKIP - user requested no commits)**
|
||||||
|
|
||||||
|
Note: Commit skipped per user request
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Self-Review
|
||||||
|
|
||||||
|
**Spec coverage:**
|
||||||
|
- ✅ Monorepo structure (Task 1)
|
||||||
|
- ✅ Bun workspaces setup (Tasks 2-3)
|
||||||
|
- ✅ Library package creation (Tasks 4-8)
|
||||||
|
- ✅ Composables structure (Task 8)
|
||||||
|
- ✅ Playground/Storybook setup (Tasks 9-15)
|
||||||
|
- ✅ Tailwind integration (Tasks 11, 14)
|
||||||
|
- ✅ PrimeVue initialization (Task 13)
|
||||||
|
- ✅ Dev workflow verification (Task 17)
|
||||||
|
- ✅ Build verification (Task 18)
|
||||||
|
- ✅ Git initialization (Task 19)
|
||||||
|
|
||||||
|
**Placeholder scan:** No TBDs, "fill in later", incomplete steps. All code blocks are complete and exact.
|
||||||
|
|
||||||
|
**Type consistency:** Package names consistent (`@nychthemeron/library`, `@nychthemeron/playground`). All import paths and config keys match across tasks.
|
||||||
|
|
||||||
|
**Scope:** Plan produces working, testable monorepo. Library builds to `dist/`, Storybook starts and loads stories. Next phase (adding components) is separate.
|
||||||
|
|
@ -0,0 +1,284 @@
|
||||||
|
# Library with Playground Design
|
||||||
|
**Date:** 2026-05-11
|
||||||
|
**Project:** nychthemeron
|
||||||
|
**Status:** Approved
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Transform nychthemeron from a single Vue app into a monorepo containing a Vue component library built on PrimeVue with Tailwind CSS styling, accompanied by a Storybook playground for interactive documentation and development.
|
||||||
|
|
||||||
|
## Goals
|
||||||
|
|
||||||
|
- Build a reusable Vue component library with custom Tailwind-based styling on PrimeVue
|
||||||
|
- Provide interactive documentation via Storybook for developers and stakeholders
|
||||||
|
- Enable fast, hot-reload development workflow
|
||||||
|
- Support both components and composables (e.g., date utilities for DatePicker)
|
||||||
|
- Keep internal/private — no npm publishing required
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### Monorepo Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
nychthemeron/
|
||||||
|
├── packages/
|
||||||
|
│ ├── library/ # Vue component library
|
||||||
|
│ │ ├── src/
|
||||||
|
│ │ │ ├── components/ # Vue components (DatePicker, Button, etc.)
|
||||||
|
│ │ │ ├── composables/ # Reusable logic (useDate, etc.)
|
||||||
|
│ │ │ └── index.ts # Barrel export for all exports
|
||||||
|
│ │ ├── __tests__/ # Unit tests
|
||||||
|
│ │ ├── package.json # Library metadata
|
||||||
|
│ │ ├── tsconfig.json # Library TS config
|
||||||
|
│ │ └── vite.config.ts # Library build config
|
||||||
|
│ │
|
||||||
|
│ └── playground/ # Storybook playground
|
||||||
|
│ ├── .storybook/
|
||||||
|
│ │ ├── main.ts # Storybook configuration
|
||||||
|
│ │ └── preview.ts # Global setup (Tailwind, PrimeVue)
|
||||||
|
│ ├── stories/ # Component stories (.stories.ts)
|
||||||
|
│ ├── package.json # Playground metadata
|
||||||
|
│ ├── tsconfig.json # Storybook TS config
|
||||||
|
│ └── tailwind.config.ts # Shared Tailwind config
|
||||||
|
│
|
||||||
|
├── package.json # Root workspace config (bun workspaces)
|
||||||
|
├── tsconfig.json # Shared base TS config
|
||||||
|
├── eslint.config.ts # Shared ESLint config
|
||||||
|
├── prettier.config.json # Shared Prettier config
|
||||||
|
└── docs/ # Documentation
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dependency Model
|
||||||
|
|
||||||
|
- **Root:** Shared dev dependencies (TypeScript, ESLint, Prettier, bun workspace config)
|
||||||
|
- **Library:** Declares PrimeVue, Vue 3 as peer dependencies; exports components + composables
|
||||||
|
- **Playground:** Imports `@nychthemeron/library` and PrimeVue; bundles Storybook
|
||||||
|
|
||||||
|
### Technology Stack
|
||||||
|
|
||||||
|
| Layer | Technology |
|
||||||
|
|-------|-----------|
|
||||||
|
| Framework | Vue 3 |
|
||||||
|
| Component Base | PrimeVue |
|
||||||
|
| Styling | Tailwind CSS + custom CSS layer |
|
||||||
|
| Build (Library) | Vite (library mode) |
|
||||||
|
| Build (Playground) | Vite + Storybook |
|
||||||
|
| Testing | Vitest + Vue Test Utils |
|
||||||
|
| Package Manager | Bun (with workspaces) |
|
||||||
|
| Type Checking | TypeScript + vue-tsc |
|
||||||
|
|
||||||
|
## Library Package Details
|
||||||
|
|
||||||
|
### Export Model
|
||||||
|
|
||||||
|
**index.ts** exports all components and composables:
|
||||||
|
```typescript
|
||||||
|
export * from './components'
|
||||||
|
export * from './composables'
|
||||||
|
```
|
||||||
|
|
||||||
|
Consumers import as:
|
||||||
|
```typescript
|
||||||
|
import { DatePicker, Button } from '@nychthemeron/library'
|
||||||
|
import { useDate } from '@nychthemeron/library'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Component Structure
|
||||||
|
|
||||||
|
Each component lives in its own folder:
|
||||||
|
```
|
||||||
|
src/components/
|
||||||
|
├── DatePicker/
|
||||||
|
│ ├── DatePicker.vue
|
||||||
|
│ └── DatePicker.spec.ts
|
||||||
|
├── Button/
|
||||||
|
│ ├── Button.vue
|
||||||
|
│ └── Button.spec.ts
|
||||||
|
└── index.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
### Composables Structure
|
||||||
|
|
||||||
|
Reusable logic in `src/composables/`:
|
||||||
|
```
|
||||||
|
src/composables/
|
||||||
|
├── useDate.ts # Date utilities (parsing, formatting, etc.)
|
||||||
|
├── index.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
Components use composables internally; consumers can also import them directly.
|
||||||
|
|
||||||
|
### Tailwind Integration
|
||||||
|
|
||||||
|
- `tailwind.config.ts` extends PrimeVue theme with custom utilities
|
||||||
|
- Library does NOT bundle Tailwind — consumers must include it in their setup
|
||||||
|
- Supports portability: teams can apply custom Tailwind themes independently
|
||||||
|
|
||||||
|
### Build Output
|
||||||
|
|
||||||
|
- ESM modules (primary) + optional CommonJS
|
||||||
|
- TypeScript declarations (`.d.ts`)
|
||||||
|
- Sourcemaps for debugging
|
||||||
|
- No dependency bundling (peer deps only)
|
||||||
|
|
||||||
|
## Playground Package Details
|
||||||
|
|
||||||
|
### Storybook Configuration
|
||||||
|
|
||||||
|
- **Addon:** Vue 3 builder
|
||||||
|
- **Global Setup:** `preview.ts` configures:
|
||||||
|
- Tailwind CSS
|
||||||
|
- PrimeVue theme setup
|
||||||
|
- Global styles
|
||||||
|
- **Stories Location:** `stories/` directory
|
||||||
|
|
||||||
|
### Story Structure
|
||||||
|
|
||||||
|
Each component gets a `.stories.ts` file:
|
||||||
|
```typescript
|
||||||
|
// stories/DatePicker.stories.ts
|
||||||
|
import DatePicker from '../components/DatePicker.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
component: DatePicker,
|
||||||
|
tags: ['autodocs'],
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Default = {}
|
||||||
|
export const WithCustomTheme = { args: { /* ... */ } }
|
||||||
|
```
|
||||||
|
|
||||||
|
Stories serve triple duty:
|
||||||
|
1. Interactive documentation for developers
|
||||||
|
2. Visual regression baseline
|
||||||
|
3. Live testing environment during dev
|
||||||
|
|
||||||
|
### Preview Configuration
|
||||||
|
|
||||||
|
`preview.ts` sets up:
|
||||||
|
- Tailwind CSS
|
||||||
|
- PrimeVue initialization
|
||||||
|
- Global CSS imports
|
||||||
|
- Example theme configuration
|
||||||
|
|
||||||
|
## Development Workflow
|
||||||
|
|
||||||
|
### Scripts
|
||||||
|
|
||||||
|
**Root-level (`bun` commands):**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dev": "bun -r --cwd packages/playground dev",
|
||||||
|
"build": "bun -r --cwd packages/library build && bun -r --cwd packages/playground build",
|
||||||
|
"lint": "eslint packages/*/src",
|
||||||
|
"type-check": "vue-tsc --build",
|
||||||
|
"test": "bun -r --cwd packages/library test:unit"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Library scripts:**
|
||||||
|
- `bun dev` — (unused for lib, optional for local testing)
|
||||||
|
- `bun build` — Vite library build
|
||||||
|
- `bun test:unit` — Vitest
|
||||||
|
|
||||||
|
**Playground scripts:**
|
||||||
|
- `bun dev` — Start Storybook dev server (port 6006)
|
||||||
|
- `bun build` — Build static Storybook site
|
||||||
|
|
||||||
|
### Developer Experience
|
||||||
|
|
||||||
|
1. **Start playground:** `bun dev` (root) → Storybook on `localhost:6006`
|
||||||
|
2. **Edit component:** Modify `packages/library/src/components/DatePicker/DatePicker.vue`
|
||||||
|
3. **See update:** Playground hot-reloads via Storybook
|
||||||
|
4. **View story:** Open `stories/DatePicker.stories.ts` to add/update examples
|
||||||
|
5. **Test:** Run `bun test` in library package
|
||||||
|
|
||||||
|
### Hot Reload
|
||||||
|
|
||||||
|
- Library changes trigger Storybook rebuild
|
||||||
|
- Playground imports `@nychthemeron/library` from source (no build step during dev)
|
||||||
|
|
||||||
|
## Testing Strategy
|
||||||
|
|
||||||
|
### Unit Tests
|
||||||
|
|
||||||
|
- **Location:** `packages/library/__tests__/`
|
||||||
|
- **Framework:** Vitest + Vue Test Utils
|
||||||
|
- **Coverage:** Component logic, composables, edge cases
|
||||||
|
- **Command:** `bun test` (from library or root)
|
||||||
|
|
||||||
|
### Visual Documentation
|
||||||
|
|
||||||
|
- Storybook stories serve as visual baseline
|
||||||
|
- Can be extended with snapshot testing if needed
|
||||||
|
|
||||||
|
### Type Safety
|
||||||
|
|
||||||
|
- `vue-tsc --build` runs across monorepo
|
||||||
|
- CI/local checks ensure no type errors
|
||||||
|
|
||||||
|
## Build & Deployment
|
||||||
|
|
||||||
|
### Library Build
|
||||||
|
|
||||||
|
- **Output:** `packages/library/dist/`
|
||||||
|
- **Formats:** ESM (primary) + optional CommonJS
|
||||||
|
- **Declarations:** Full TypeScript support
|
||||||
|
- **Size:** Optimized with tree-shaking enabled
|
||||||
|
|
||||||
|
### Playground Build
|
||||||
|
|
||||||
|
- **Output:** `packages/playground/storybook-static/`
|
||||||
|
- **Deploy:** Can be served on internal server, wiki, or documentation site
|
||||||
|
- **Audience:** Internal team, stakeholders, designers
|
||||||
|
|
||||||
|
### Internal Distribution
|
||||||
|
|
||||||
|
No npm publishing. Teams use library by:
|
||||||
|
- Cloning/pulling monorepo
|
||||||
|
- Installing dependencies: `bun install`
|
||||||
|
- Importing components from `@nychthemeron/library`
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
### Library Package
|
||||||
|
|
||||||
|
**Peer Dependencies:**
|
||||||
|
- `vue@^3.5.0`
|
||||||
|
- `primevue@^18.0.0` (or latest)
|
||||||
|
|
||||||
|
**Dev Dependencies:** (shared from root)
|
||||||
|
- TypeScript, Vite, ESLint, Prettier
|
||||||
|
|
||||||
|
### Playground Package
|
||||||
|
|
||||||
|
**Dependencies:**
|
||||||
|
- `@nychthemeron/library` (local workspace package)
|
||||||
|
- `primevue`
|
||||||
|
- `vue`
|
||||||
|
|
||||||
|
**Dev Dependencies:**
|
||||||
|
- Storybook (@storybook/vue3)
|
||||||
|
- Vite, TypeScript
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
|
||||||
|
- ✅ Monorepo structure set up with bun workspaces
|
||||||
|
- ✅ Library builds to `dist/` with exports
|
||||||
|
- ✅ Storybook starts with `bun dev` and hot-reloads on library changes
|
||||||
|
- ✅ Components use PrimeVue + Tailwind CSS
|
||||||
|
- ✅ Composables (e.g., `useDate`) are accessible
|
||||||
|
- ✅ TypeScript declarations present and correct
|
||||||
|
- ✅ Unit tests run and pass
|
||||||
|
- ✅ Shared config (ESLint, Prettier, tsconfig) enforced across packages
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. Restructure project into monorepo layout
|
||||||
|
2. Set up bun workspaces in root `package.json`
|
||||||
|
3. Create library package with Vite lib config
|
||||||
|
4. Create playground package with Storybook
|
||||||
|
5. Add shared configurations
|
||||||
|
6. Implement first component (e.g., Button or DatePicker)
|
||||||
|
7. Create corresponding Storybook stories
|
||||||
|
8. Test hot-reload workflow
|
||||||
3
env.d.ts
vendored
Normal file
3
env.d.ts
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
|
declare module '*.css' {}
|
||||||
32
eslint.config.ts
Normal file
32
eslint.config.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { globalIgnores } from 'eslint/config'
|
||||||
|
import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript'
|
||||||
|
import pluginVue from 'eslint-plugin-vue'
|
||||||
|
import pluginVitest from '@vitest/eslint-plugin'
|
||||||
|
import pluginOxlint from 'eslint-plugin-oxlint'
|
||||||
|
import skipFormatting from 'eslint-config-prettier/flat'
|
||||||
|
|
||||||
|
// To allow more languages other than `ts` in `.vue` files, uncomment the following lines:
|
||||||
|
// import { configureVueProject } from '@vue/eslint-config-typescript'
|
||||||
|
// configureVueProject({ scriptLangs: ['ts', 'tsx'] })
|
||||||
|
// More info at https://github.com/vuejs/eslint-config-typescript/#advanced-setup
|
||||||
|
|
||||||
|
export default defineConfigWithVueTs(
|
||||||
|
{
|
||||||
|
name: 'app/files-to-lint',
|
||||||
|
files: ['**/*.{vue,ts,mts,tsx}'],
|
||||||
|
},
|
||||||
|
|
||||||
|
globalIgnores(['**/dist/**', '**/dist-ssr/**', '**/coverage/**']),
|
||||||
|
|
||||||
|
...pluginVue.configs['flat/essential'],
|
||||||
|
vueTsConfigs.recommended,
|
||||||
|
|
||||||
|
{
|
||||||
|
...pluginVitest.configs.recommended,
|
||||||
|
files: ['src/**/__tests__/*'],
|
||||||
|
},
|
||||||
|
|
||||||
|
...pluginOxlint.buildFromOxlintConfigFile('.oxlintrc.json'),
|
||||||
|
|
||||||
|
skipFormatting,
|
||||||
|
)
|
||||||
47
package.json
Normal file
47
package.json
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
{
|
||||||
|
"name": "nychthemeron",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"workspaces": [
|
||||||
|
"packages/*"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"dev": "cd packages/playground && bun run dev",
|
||||||
|
"build": "cd packages/library && bun run build && cd ../playground && bun run build",
|
||||||
|
"preview": "cd packages/playground && bun run preview",
|
||||||
|
"type-check": "vue-tsc --build",
|
||||||
|
"lint": "eslint packages/*/src --fix",
|
||||||
|
"format": "prettier --write --experimental-cli packages/*/src",
|
||||||
|
"test": "cd packages/library && bun run test:unit"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@tsconfig/node24": "^24.0.4",
|
||||||
|
"@types/jsdom": "^28.0.1",
|
||||||
|
"@types/node": "^24.12.2",
|
||||||
|
"@vitejs/plugin-vue": "^6.0.6",
|
||||||
|
"primevue": "^4.5.0",
|
||||||
|
"@vitest/eslint-plugin": "^1.6.16",
|
||||||
|
"@vue/eslint-config-typescript": "^14.7.0",
|
||||||
|
"@vue/test-utils": "^2.4.6",
|
||||||
|
"@vue/tsconfig": "^0.9.1",
|
||||||
|
"eslint": "^10.2.1",
|
||||||
|
"eslint-config-prettier": "^10.1.8",
|
||||||
|
"eslint-plugin-oxlint": "~1.60.0",
|
||||||
|
"eslint-plugin-vue": "~10.8.0",
|
||||||
|
"jiti": "^2.6.1",
|
||||||
|
"jsdom": "^29.0.2",
|
||||||
|
"npm-run-all2": "^8.0.4",
|
||||||
|
"oxlint": "~1.60.0",
|
||||||
|
"prettier": "3.8.3",
|
||||||
|
"typescript": "~6.0.0",
|
||||||
|
"vite": "^8.0.8",
|
||||||
|
"vite-plugin-vue-devtools": "^8.1.1",
|
||||||
|
"vitest": "^4.1.4",
|
||||||
|
"vue": "^3.5.32",
|
||||||
|
"vue-tsc": "^3.2.6"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
37
packages/library/package.json
Normal file
37
packages/library/package.json
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
{
|
||||||
|
"name": "@nychthemeron/library",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"type": "module",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"import": "./dist/index.js"
|
||||||
|
},
|
||||||
|
"./components": {
|
||||||
|
"import": "./src/components/index.ts"
|
||||||
|
},
|
||||||
|
"./style": {
|
||||||
|
"import": "./src/assets/css/nychthemeron.css",
|
||||||
|
"types": "./src/index.d.ts"
|
||||||
|
},
|
||||||
|
"./theme": {
|
||||||
|
"import": "./src/assets/theme/theme.css",
|
||||||
|
"types": "./src/index.d.ts"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist",
|
||||||
|
"src/assets"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"build": "vite build",
|
||||||
|
"test:unit": "vitest"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": "^3.5.0",
|
||||||
|
"primevue": "^4.5.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitest/coverage-v8": "^2.0.0",
|
||||||
|
"@vue/test-utils": "^2.4.6"
|
||||||
|
}
|
||||||
|
}
|
||||||
149
packages/library/src/assets/css/nychthemeron.css
Normal file
149
packages/library/src/assets/css/nychthemeron.css
Normal file
|
|
@ -0,0 +1,149 @@
|
||||||
|
/* ============================================================
|
||||||
|
nychthemeron.css
|
||||||
|
Toggle themes by setting data-theme on <html>:
|
||||||
|
<html data-theme="hades">
|
||||||
|
<html data-theme="apollo">
|
||||||
|
============================================================ */
|
||||||
|
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@400;600&family=IBM+Plex+Mono:wght@400;500&family=IBM+Plex+Sans:wght@400;500&display=swap');
|
||||||
|
|
||||||
|
/* ── HADES ───────────────────────────────────────────────── */
|
||||||
|
[data-theme='hades'] {
|
||||||
|
/* surfaces */
|
||||||
|
--surface-0: #18160f;
|
||||||
|
--surface-1: #28241a;
|
||||||
|
--surface-2: #2e2a1e;
|
||||||
|
--surface-3: #322e22;
|
||||||
|
--track: #32302a;
|
||||||
|
|
||||||
|
/* borders */
|
||||||
|
--border: #4a4538;
|
||||||
|
--border-hi: #5a5040;
|
||||||
|
--border-lo: #38342a;
|
||||||
|
|
||||||
|
/* primary */
|
||||||
|
--primary: #d4b278;
|
||||||
|
--primary-fg: #0e0c08;
|
||||||
|
|
||||||
|
/* semantic */
|
||||||
|
--warn: #e08840; /* phlegethon */
|
||||||
|
--warn-subtle: #281808;
|
||||||
|
--warn-border: #4a2c10;
|
||||||
|
|
||||||
|
--danger: #9ab4c8; /* styx */
|
||||||
|
--danger-subtle: #141a22;
|
||||||
|
--danger-border: #2a3848;
|
||||||
|
|
||||||
|
--info: #88aec8; /* cocytus */
|
||||||
|
--info-subtle: #101e2c;
|
||||||
|
--info-border: #1e3850;
|
||||||
|
|
||||||
|
--muted: #c8c0a8; /* lethe */
|
||||||
|
--muted-subtle: #201e18;
|
||||||
|
--muted-border: #403c30;
|
||||||
|
|
||||||
|
--neutral: #7a6e60; /* acheron */
|
||||||
|
|
||||||
|
--success: #6aaa7a; /* laurel */
|
||||||
|
--success-subtle: #0e2214;
|
||||||
|
--success-border: #1e4228;
|
||||||
|
|
||||||
|
/* text */
|
||||||
|
--text-high: #f4f0e8;
|
||||||
|
--text-body: #d0c8b8;
|
||||||
|
--text-muted: #908878;
|
||||||
|
--text-dim: #807868;
|
||||||
|
--text-label: #c0b8a8;
|
||||||
|
|
||||||
|
/* focus ring */
|
||||||
|
--focus-ring: 0 0 0 3px rgba(212, 178, 120, 0.18);
|
||||||
|
|
||||||
|
/* fonts */
|
||||||
|
--font-serif: 'Cinzel', Georgia, serif;
|
||||||
|
--font-sans: 'IBM Plex Sans', system-ui, sans-serif;
|
||||||
|
--font-mono: 'IBM Plex Mono', monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── APOLLO ──────────────────────────────────────────────── */
|
||||||
|
[data-theme='apollo'] {
|
||||||
|
/* surfaces */
|
||||||
|
--surface-0: #faf7f2;
|
||||||
|
--surface-1: #f4f0e8;
|
||||||
|
--surface-2: #eee9df;
|
||||||
|
--surface-3: #e4ddd0;
|
||||||
|
--track: #e4ddd0;
|
||||||
|
|
||||||
|
/* borders */
|
||||||
|
--border: #bab2a0;
|
||||||
|
--border-hi: #c0b8a8;
|
||||||
|
--border-lo: #ccc4b4;
|
||||||
|
|
||||||
|
/* primary */
|
||||||
|
--primary: #b8860b;
|
||||||
|
--primary-fg: #fff8e8;
|
||||||
|
|
||||||
|
/* semantic */
|
||||||
|
--warn: #9a6020; /* harmony */
|
||||||
|
--warn-subtle: #fdf3e0;
|
||||||
|
--warn-border: #e8d0a0;
|
||||||
|
|
||||||
|
--danger: #7a1a1a; /* plague */
|
||||||
|
--danger-subtle: #faeaea;
|
||||||
|
--danger-border: #c8a0a0;
|
||||||
|
|
||||||
|
--info: #3a5a8a; /* oracle */
|
||||||
|
--info-subtle: #e8eef8;
|
||||||
|
--info-border: #b0c4e0;
|
||||||
|
|
||||||
|
--muted: #a09080; /* lethe */
|
||||||
|
--muted-subtle: #ede8de;
|
||||||
|
--muted-border: #d4cdc0;
|
||||||
|
|
||||||
|
--neutral: #8a8070; /* marble */
|
||||||
|
|
||||||
|
--success: #5a7a40; /* laurel */
|
||||||
|
--success-subtle: #eef4e8;
|
||||||
|
--success-border: #c0d4a8;
|
||||||
|
|
||||||
|
/* text */
|
||||||
|
--text-high: #1a1610;
|
||||||
|
--text-body: #3a3020;
|
||||||
|
--text-muted: #5a5040;
|
||||||
|
--text-dim: #8a8070;
|
||||||
|
--text-label: #5a5040;
|
||||||
|
|
||||||
|
/* focus ring */
|
||||||
|
--focus-ring: 0 0 0 3px rgba(184, 134, 11, 0.12);
|
||||||
|
|
||||||
|
/* fonts */
|
||||||
|
--font-serif: 'Cinzel', Georgia, serif;
|
||||||
|
--font-sans: 'IBM Plex Sans', system-ui, sans-serif;
|
||||||
|
--font-mono: 'IBM Plex Mono', monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── BASE RESET ──────────────────────────────────────────── */
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
font-size: 14px;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: var(--surface-0);
|
||||||
|
color: var(--text-body);
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
*:focus-visible {
|
||||||
|
outline: none;
|
||||||
|
box-shadow: var(--focus-ring);
|
||||||
|
}
|
||||||
6
packages/library/src/assets/theme/theme.css
Normal file
6
packages/library/src/assets/theme/theme.css
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
@import url('../css/nychthemeron.css');
|
||||||
|
|
||||||
|
.nych-button-primary {
|
||||||
|
background-color: var(--primary);
|
||||||
|
color: var(--primary-fg);
|
||||||
|
}
|
||||||
41
packages/library/src/components/NychButton.vue
Normal file
41
packages/library/src/components/NychButton.vue
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Button } from 'primevue'
|
||||||
|
import { computed } from 'vue'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
label?: string
|
||||||
|
variant?: 'primary' | 'secondary' | 'success' | 'warning' | 'danger'
|
||||||
|
size?: 'small' | 'normal' | 'large'
|
||||||
|
disabled?: boolean
|
||||||
|
loading?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
label: 'Button',
|
||||||
|
variant: 'primary',
|
||||||
|
size: 'normal',
|
||||||
|
disabled: false,
|
||||||
|
loading: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
click: [event: MouseEvent]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const buttonClass = computed(() => ({
|
||||||
|
[`nych-button-${props.variant}`]: true,
|
||||||
|
}))
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Button
|
||||||
|
unstyled
|
||||||
|
:label="label"
|
||||||
|
:class="buttonClass"
|
||||||
|
:disabled="disabled || loading"
|
||||||
|
:loading="loading"
|
||||||
|
@click="emit('click', $event)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="css"></style>
|
||||||
3
packages/library/src/components/index.ts
Normal file
3
packages/library/src/components/index.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
// Re-export all components here as they're added
|
||||||
|
export { default as NychButton } from './NychButton.vue'
|
||||||
|
// export { default as DatePicker } from './DatePicker/DatePicker.vue'
|
||||||
2
packages/library/src/composables/index.ts
Normal file
2
packages/library/src/composables/index.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
// Re-export all composables here as they're added
|
||||||
|
// export { useDate } from './useDate'
|
||||||
2
packages/library/src/index.d.ts
vendored
Normal file
2
packages/library/src/index.d.ts
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
declare module '@nychthemeron/library/style';
|
||||||
|
declare module '@nychthemeron/library/theme';
|
||||||
2
packages/library/src/index.ts
Normal file
2
packages/library/src/index.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
export * from '~/components'
|
||||||
|
//export * from '~/composables'
|
||||||
11
packages/library/tests/App.spec.ts
Normal file
11
packages/library/tests/App.spec.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { describe, it, expect } from 'vitest'
|
||||||
|
|
||||||
|
import { mount } from '@vue/test-utils'
|
||||||
|
import App from '~/App.vue'
|
||||||
|
|
||||||
|
describe('App', () => {
|
||||||
|
it('mounts renders properly', () => {
|
||||||
|
const wrapper = mount(App)
|
||||||
|
expect(wrapper.text()).toContain('You did it!')
|
||||||
|
})
|
||||||
|
})
|
||||||
16
packages/library/tsconfig.json
Normal file
16
packages/library/tsconfig.json
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": ".",
|
||||||
|
"outDir": "./dist",
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"emitDeclarationOnly": false,
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"],
|
||||||
|
"~/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts", "src/**/*.vue", "tests/**/*.ts"],
|
||||||
|
"exclude": ["dist"]
|
||||||
|
}
|
||||||
29
packages/library/vite.config.ts
Normal file
29
packages/library/vite.config.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [vue()],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'~': fileURLToPath(new URL('./src', import.meta.url)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
build: {
|
||||||
|
lib: {
|
||||||
|
entry: fileURLToPath(new URL('./src/index.ts', import.meta.url)),
|
||||||
|
name: 'NychthemeronLibrary',
|
||||||
|
formats: ['es'],
|
||||||
|
fileName: () => 'index.js',
|
||||||
|
},
|
||||||
|
rollupOptions: {
|
||||||
|
external: ['vue', 'primevue'],
|
||||||
|
output: {
|
||||||
|
globals: {
|
||||||
|
vue: 'Vue',
|
||||||
|
primevue: 'PrimeVue',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
17
packages/library/vitest.config.ts
Normal file
17
packages/library/vitest.config.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { defineConfig } from 'vitest/config'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [vue()],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'~': fileURLToPath(new URL('./src', import.meta.url)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
test: {
|
||||||
|
globals: true,
|
||||||
|
environment: 'jsdom',
|
||||||
|
include: ['**/*.spec.ts'],
|
||||||
|
},
|
||||||
|
})
|
||||||
25
packages/playground/.storybook/main.ts
Normal file
25
packages/playground/.storybook/main.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
import type { StorybookConfig } from '@storybook/vue3-vite'
|
||||||
|
|
||||||
|
const config: StorybookConfig = {
|
||||||
|
stories: ['../stories/**/*.stories.ts'],
|
||||||
|
addons: [
|
||||||
|
'@storybook/addon-essentials',
|
||||||
|
'@storybook/addon-interactions',
|
||||||
|
'@storybook/addon-links',
|
||||||
|
],
|
||||||
|
framework: {
|
||||||
|
name: '@storybook/vue3-vite',
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
core: {
|
||||||
|
disableTelemetry: true,
|
||||||
|
},
|
||||||
|
viteFinal: (config) => {
|
||||||
|
if (config.build) {
|
||||||
|
config.build.chunkSizeWarningLimit = 1000
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default config
|
||||||
54
packages/playground/.storybook/preview.ts
Normal file
54
packages/playground/.storybook/preview.ts
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
import type { Preview } from '@storybook/vue3'
|
||||||
|
import '@nychthemeron/library/theme'
|
||||||
|
|
||||||
|
const preview: Preview = {
|
||||||
|
parameters: {
|
||||||
|
layout: 'centered',
|
||||||
|
toolbar: {
|
||||||
|
theme: {
|
||||||
|
items: [
|
||||||
|
{ value: 'hades', title: 'Dark (Hades)', left: '🌙' },
|
||||||
|
{ value: 'apollo', title: 'Light (Apollo)', left: '☀️' },
|
||||||
|
],
|
||||||
|
dynamicTitle: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
component: 'Component documentation',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
decorators: [
|
||||||
|
(story, context) => {
|
||||||
|
const theme = context.parameters.toolbar?.theme
|
||||||
|
? context.globals.theme || 'hades'
|
||||||
|
: 'hades'
|
||||||
|
|
||||||
|
return {
|
||||||
|
setup() {
|
||||||
|
document.documentElement.setAttribute('data-theme', theme)
|
||||||
|
},
|
||||||
|
components: { story },
|
||||||
|
template: '<story />',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
globalTypes: {
|
||||||
|
theme: {
|
||||||
|
name: 'Theme',
|
||||||
|
description: 'Global theme for all components',
|
||||||
|
defaultValue: 'hades',
|
||||||
|
toolbar: {
|
||||||
|
icon: 'paintbrush',
|
||||||
|
items: [
|
||||||
|
{ value: 'hades', title: 'Dark (Hades)', left: '🌙' },
|
||||||
|
{ value: 'apollo', title: 'Light (Apollo)', left: '☀️' },
|
||||||
|
],
|
||||||
|
dynamicTitle: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default preview
|
||||||
26
packages/playground/package.json
Normal file
26
packages/playground/package.json
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"name": "@nychthemeron/playground",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "storybook dev -p 6006",
|
||||||
|
"build": "storybook build",
|
||||||
|
"preview": "npx vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@nychthemeron/library": "workspace:*",
|
||||||
|
"primevue": "^4.5.0",
|
||||||
|
"vue": "^3.5.32"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@storybook/addon-essentials": "^8.0.0",
|
||||||
|
"@storybook/addon-interactions": "^8.0.0",
|
||||||
|
"@storybook/addon-links": "^8.0.0",
|
||||||
|
"@storybook/blocks": "^8.0.0",
|
||||||
|
"@storybook/vue3": "^8.0.0",
|
||||||
|
"@storybook/vue3-vite": "^8.0.0",
|
||||||
|
"storybook": "^8.0.0",
|
||||||
|
"typescript": "~6.0.0",
|
||||||
|
"vue": "^3.5.32"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
packages/playground/public/favicon.ico
Normal file
BIN
packages/playground/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
134
packages/playground/stories/Button.stories.ts
Normal file
134
packages/playground/stories/Button.stories.ts
Normal file
|
|
@ -0,0 +1,134 @@
|
||||||
|
import type { Meta, StoryObj } from '@storybook/vue3'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { NychButton } from '@nychthemeron/library/components'
|
||||||
|
|
||||||
|
const meta = {
|
||||||
|
title: 'Components/Button',
|
||||||
|
component: NychButton,
|
||||||
|
parameters: {
|
||||||
|
layout: 'centered',
|
||||||
|
},
|
||||||
|
tags: ['autodocs'],
|
||||||
|
argTypes: {
|
||||||
|
variant: {
|
||||||
|
control: { type: 'select' },
|
||||||
|
options: ['primary', 'secondary', 'success', 'warning', 'danger'],
|
||||||
|
description: 'Button variant style',
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
control: { type: 'select' },
|
||||||
|
options: ['small', 'normal', 'large'],
|
||||||
|
description: 'Button size',
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
control: { type: 'boolean' },
|
||||||
|
description: 'Disable the button',
|
||||||
|
},
|
||||||
|
loading: {
|
||||||
|
control: { type: 'boolean' },
|
||||||
|
description: 'Show loading state',
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
control: { type: 'text' },
|
||||||
|
description: 'Button label text',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} satisfies Meta<typeof NychButton>
|
||||||
|
|
||||||
|
export default meta
|
||||||
|
type Story = StoryObj<typeof meta>
|
||||||
|
|
||||||
|
export const Primary: Story = {
|
||||||
|
args: {
|
||||||
|
label: 'Primary Button',
|
||||||
|
variant: 'primary',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Secondary: Story = {
|
||||||
|
args: {
|
||||||
|
label: 'Secondary Button',
|
||||||
|
variant: 'secondary',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Success: Story = {
|
||||||
|
args: {
|
||||||
|
label: 'Success Button',
|
||||||
|
variant: 'success',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Warning: Story = {
|
||||||
|
args: {
|
||||||
|
label: 'Warning Button',
|
||||||
|
variant: 'warning',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Danger: Story = {
|
||||||
|
args: {
|
||||||
|
label: 'Danger Button',
|
||||||
|
variant: 'danger',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Disabled: Story = {
|
||||||
|
args: {
|
||||||
|
label: 'Disabled Button',
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Loading: Story = {
|
||||||
|
args: {
|
||||||
|
label: 'Loading Button',
|
||||||
|
loading: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Sizes: Story = {
|
||||||
|
render: () => ({
|
||||||
|
components: { NychButton },
|
||||||
|
setup() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<div class="flex gap-4 items-center">
|
||||||
|
<NychButton label="Small" size="small" />
|
||||||
|
<NychButton label="Normal" size="normal" />
|
||||||
|
<NychButton label="Large" size="large" />
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Interactive: Story = {
|
||||||
|
render: () => ({
|
||||||
|
components: { NychButton },
|
||||||
|
setup() {
|
||||||
|
const clickCount = ref(0)
|
||||||
|
const isLoading = ref(false)
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
clickCount.value++
|
||||||
|
isLoading.value = true
|
||||||
|
setTimeout(() => {
|
||||||
|
isLoading.value = false
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
return { clickCount, isLoading, handleClick }
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<div class="text-center">
|
||||||
|
<NychButton
|
||||||
|
label="Click Me"
|
||||||
|
@click="handleClick"
|
||||||
|
:loading="isLoading"
|
||||||
|
/>
|
||||||
|
<p class="mt-4 text-lg">Clicks: {{ clickCount }}</p>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
}),
|
||||||
|
}
|
||||||
27
packages/playground/stories/Example.stories.ts
Normal file
27
packages/playground/stories/Example.stories.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
import type { Meta, StoryObj } from '@storybook/vue3'
|
||||||
|
|
||||||
|
const meta = {
|
||||||
|
title: 'Example/Welcome',
|
||||||
|
parameters: {
|
||||||
|
layout: 'centered',
|
||||||
|
},
|
||||||
|
tags: ['autodocs'],
|
||||||
|
} satisfies Meta
|
||||||
|
|
||||||
|
export default meta
|
||||||
|
type Story = StoryObj<typeof meta>
|
||||||
|
|
||||||
|
export const Welcome: Story = {
|
||||||
|
render: () => ({
|
||||||
|
template: `
|
||||||
|
<div class="p-8 text-center">
|
||||||
|
<h1 class="text-3xl font-bold text-primary-500 mb-4">
|
||||||
|
Nychthemeron Component Library
|
||||||
|
</h1>
|
||||||
|
<p class="text-gray-600">
|
||||||
|
Interactive playground for Vue components with PrimeVue + Tailwind
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
}),
|
||||||
|
}
|
||||||
22
packages/playground/tsconfig.json
Normal file
22
packages/playground/tsconfig.json
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": false,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"lib": [
|
||||||
|
"dom",
|
||||||
|
"dom.iterable",
|
||||||
|
"esnext"
|
||||||
|
],
|
||||||
|
"rootDir": "."
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"../../env.d.ts",
|
||||||
|
".storybook/**/*.ts",
|
||||||
|
".storybook/**/*.tsx",
|
||||||
|
".storybook/**/*.vue",
|
||||||
|
"stories/**/*.ts",
|
||||||
|
"stories/**/*.tsx",
|
||||||
|
"stories/**/*.vue"
|
||||||
|
]
|
||||||
|
}
|
||||||
12
packages/playground/vite.config.ts
Normal file
12
packages/playground/vite.config.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [vue()],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'~': fileURLToPath(new URL('./src', import.meta.url)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
8
tsconfig.json
Normal file
8
tsconfig.json
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"files": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.node.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
27
tsconfig.node.json
Normal file
27
tsconfig.node.json
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
// TSConfig for modules that run in Node.js environment via either transpilation or type-stripping.
|
||||||
|
{
|
||||||
|
"extends": "@tsconfig/node24/tsconfig.json",
|
||||||
|
"include": [
|
||||||
|
"vite.config.*",
|
||||||
|
"vitest.config.*",
|
||||||
|
"cypress.config.*",
|
||||||
|
"playwright.config.*",
|
||||||
|
"eslint.config.*"
|
||||||
|
],
|
||||||
|
"compilerOptions": {
|
||||||
|
// Most tools use transpilation instead of Node.js's native type-stripping.
|
||||||
|
// Bundler mode provides a smoother developer experience.
|
||||||
|
"module": "preserve",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
|
||||||
|
// Include Node.js types and avoid accidentally including other `@types/*` packages.
|
||||||
|
"types": ["node"],
|
||||||
|
|
||||||
|
// Disable emitting output during `vue-tsc --build`, which is used for type-checking only.
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
// `vue-tsc --build` produces a .tsbuildinfo file for incremental type-checking.
|
||||||
|
// Specified here to keep it out of the root directory.
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo"
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue