commit 3a338b33dddb16d1fb55d29974d70384893c732a Author: saridsa2 Date: Mon Mar 16 14:23:23 2026 +0530 chore: initial Untitled UI Vite scaffold with FontAwesome Pro diff --git a/.github/last-sync-commit b/.github/last-sync-commit new file mode 100644 index 0000000..a7b3ea1 --- /dev/null +++ b/.github/last-sync-commit @@ -0,0 +1 @@ +3cdabc0b462049086ebc0f954fc661dc4ca2a594 diff --git a/.github/workflows/README.md b/.github/workflows/README.md new file mode 100644 index 0000000..a93df5d --- /dev/null +++ b/.github/workflows/README.md @@ -0,0 +1,73 @@ +# Component sync workflow + +This workflow synchronizes components from the main [untitleduico/react](https://github.com/untitleduico/react) repository with intelligent commit tracking. + +## How to use + +1. Go to the "Actions" tab in your GitHub repository +2. Select "Sync Components from Main Repository" +3. Click "Run workflow" +4. Configure options: + - **Directories**: Which directories to sync (default: `components,hooks,utils,styles`) + - **Sync mode**: Choose between `all` or `existing-only` +5. Click "Run workflow" to start the sync + +## What it does + +1. **Fetches** the latest commit information from the main repository +2. **Tracks** previously synced commits to show what's new +3. **Creates** a branch named `sync/YYYY-MM-DD-[commit-hash]` +4. **Processes** files by removing `"use client";` directives +5. **Generates** a PR with commit history and detailed diff summaries +6. **Stores** the sync state for future runs + +## Sync modes + +### `all` (default) +- Syncs all files from the main repository +- Adds new files and updates existing ones +- Creates complete mirror of main repo structure + +### `existing-only` +- Only updates files that already exist in your repository +- Skips new files from the main repository +- Useful for maintaining a subset of components + +## Smart commit tracking + +- **First run**: Shows "Initial sync" with latest commit details +- **Subsequent runs**: Shows commit range since last sync +- **Up-to-date**: Indicates when no new commits are available +- **Commit history**: Collapsible list of all commits since last sync + +## Branch naming + +Branches follow the pattern: `sync/YYYY-MM-DD-[short-commit-hash]` +- Example: `sync/2024-03-15-a1b2c3d` + +## PR format + +The generated PR includes: + +### Title +- **New commits**: `πŸ”„ Sync components (X commits) β†’ a1b2c3d` +- **First sync**: `πŸŽ‰ Initial sync from main repository β†’ a1b2c3d` + +### Content +- **Commits since last sync**: Collapsible commit history with links +- **Changed files**: Each file with collapsible diff view +- **Sync details**: Source repo, latest commit, date, directories synced +- **Automated processing**: Notes about `"use client"` removal + +## State management + +The workflow stores sync state in `.github/last-sync-commit` to track: +- Last successfully synced commit hash +- Enables incremental sync reporting +- Shows commit ranges in PR descriptions + +## Requirements + +- Repository must have `contents: write` and `pull-requests: write` permissions +- Uses `GITHUB_TOKEN` (automatically available in GitHub Actions) +- GitHub CLI (`gh`) for PR creation \ No newline at end of file diff --git a/.github/workflows/sync-components.yml b/.github/workflows/sync-components.yml new file mode 100644 index 0000000..c5a828c --- /dev/null +++ b/.github/workflows/sync-components.yml @@ -0,0 +1,380 @@ +name: Sync Components from Main Repository + +on: + workflow_dispatch: + inputs: + sync_directories: + description: "Directories to sync (comma-separated)" + required: false + default: "components,hooks,utils,styles" + type: string + sync_mode: + description: "Sync mode" + required: false + default: "all" + type: choice + options: + - all + - existing-only + +jobs: + sync-components: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "18" + + - name: Configure git + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + + - name: Fetch main repository and get commit info + id: fetch-main-repo + run: | + echo "Fetching main repository..." + + # Create a temporary directory for cloning + mkdir -p /tmp/main-repo + cd /tmp/main-repo + + # Clone the main repository + git clone https://github.com/untitleduico/react.git . + + # Get latest commit info + LATEST_COMMIT_HASH=$(git rev-parse HEAD) + LATEST_COMMIT_SHORT=$(git rev-parse --short HEAD) + LATEST_COMMIT_MESSAGE=$(git log -1 --pretty=format:"%s") + LATEST_COMMIT_AUTHOR=$(git log -1 --pretty=format:"%an") + LATEST_COMMIT_DATE=$(git log -1 --pretty=format:"%ci") + + echo "LATEST_COMMIT_HASH=$LATEST_COMMIT_HASH" >> $GITHUB_ENV + echo "LATEST_COMMIT_SHORT=$LATEST_COMMIT_SHORT" >> $GITHUB_ENV + echo "LATEST_COMMIT_MESSAGE=$LATEST_COMMIT_MESSAGE" >> $GITHUB_ENV + echo "LATEST_COMMIT_AUTHOR=$LATEST_COMMIT_AUTHOR" >> $GITHUB_ENV + echo "LATEST_COMMIT_DATE=$LATEST_COMMIT_DATE" >> $GITHUB_ENV + + echo "Latest commit: $LATEST_COMMIT_SHORT - $LATEST_COMMIT_MESSAGE" + + # Get the directory structure (main repo has directories at root level, not in src/) + # Exclude demo and story files + find components hooks utils styles -type f ! -name "*.demo.tsx" ! -name "*.story.tsx" ! -name "*.stories.tsx" 2>/dev/null | sort > /tmp/main-repo-files.txt || echo "Some directories may not exist" + + echo "Files to sync:" + cat /tmp/main-repo-files.txt + + - name: Get last synced commit + id: get-last-sync + run: | + cd $GITHUB_WORKSPACE + + # Check if we have a previous sync record + if [ -f ".github/last-sync-commit" ] && [ -s ".github/last-sync-commit" ]; then + # Read the first line and trim whitespace, ignore comments + LAST_SYNC_COMMIT=$(head -n 1 .github/last-sync-commit | xargs | grep -v '^#' || echo "") + + if [ -n "$LAST_SYNC_COMMIT" ] && [ "$LAST_SYNC_COMMIT" != "" ]; then + echo "LAST_SYNC_COMMIT=$LAST_SYNC_COMMIT" >> $GITHUB_ENV + echo "Found previous sync commit: $LAST_SYNC_COMMIT" + else + echo "LAST_SYNC_COMMIT=" >> $GITHUB_ENV + echo "Sync file exists but no valid commit hash found" + fi + else + echo "LAST_SYNC_COMMIT=" >> $GITHUB_ENV + echo "No previous sync record found" + fi + + - name: Get commits since last sync + id: get-commits-since-sync + run: | + cd /tmp/main-repo + + if [ -n "$LAST_SYNC_COMMIT" ]; then + echo "Getting commits since $LAST_SYNC_COMMIT..." + + # Get commit log since last sync + git log --oneline "$LAST_SYNC_COMMIT".."$LATEST_COMMIT_HASH" > /tmp/commits-since-sync.txt || echo "Could not get commit range" + + # Count commits + COMMIT_COUNT=$(wc -l < /tmp/commits-since-sync.txt) + echo "COMMIT_COUNT=$COMMIT_COUNT" >> $GITHUB_ENV + + echo "Commits since last sync ($COMMIT_COUNT):" + cat /tmp/commits-since-sync.txt + else + echo "COMMIT_COUNT=unknown" >> $GITHUB_ENV + echo "No previous sync - this is the first sync" + echo "First sync - syncing from latest commit" > /tmp/commits-since-sync.txt + fi + + - name: Create sync branch + run: | + cd $GITHUB_WORKSPACE + BRANCH_NAME="sync/$(date +%Y-%m-%d-%H%M%S)-$LATEST_COMMIT_SHORT" + echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV + + # Delete branch if it exists locally + git branch -D "$BRANCH_NAME" 2>/dev/null || true + + # Delete branch if it exists remotely + git push origin --delete "$BRANCH_NAME" 2>/dev/null || true + + # Create fresh branch + git checkout -b "$BRANCH_NAME" + echo "Created branch: $BRANCH_NAME" + + - name: Sync files from main repository + run: | + cd /tmp/main-repo + + # Read the sync directories from input + IFS=',' read -ra DIRS <<< "${{ github.event.inputs.sync_directories }}" + + # Create sync summary file + echo "# Sync Summary" > /tmp/sync-summary.md + echo "" >> /tmp/sync-summary.md + + for dir in "${DIRS[@]}"; do + dir=$(echo "$dir" | xargs) # trim whitespace + echo "## Syncing $dir directory" >> /tmp/sync-summary.md + + # Main repo has directories at root level, target repo has them in src/ + if [ -d "$dir" ]; then + # Create directory in target repo if it doesn't exist + mkdir -p "$GITHUB_WORKSPACE/src/$dir" + + # Find all files in the directory from main repo, excluding demo and story files + find "$dir" -type f ! -name "*.demo.tsx" ! -name "*.story.tsx" ! -name "*.stories.tsx" | while read -r file; do + target_file="$GITHUB_WORKSPACE/src/$file" + + # Check sync mode + if [ "${{ github.event.inputs.sync_mode }}" = "existing-only" ]; then + # Only process if target file already exists + if [ ! -f "$target_file" ]; then + echo "Skipping new file: $file (existing-only mode)" + continue + fi + fi + + echo "Processing $file..." + + # Copy file content and process it + content=$(cat "$file") + + # Remove "use client" directive if present (including double newlines after it) + # This handles both double quotes and single quotes, with optional semicolon + processed_content=$(echo "$content" | sed -E ' + /^"use client";?\s*$/{ + N + N + s/"use client";?\s*\n\s*\n// + } + /^'\''use client'\'';?\s*$/{ + N + N + s/'\''use client'\'';?\s*\n\s*\n// + } + ') + + # Target file goes in src/ directory + target_dir=$(dirname "$target_file") + mkdir -p "$target_dir" + + # Write processed content to target file + echo "$processed_content" > "$target_file" + + echo "- src/$file" >> /tmp/sync-summary.md + done + + # Log skipped files for transparency + DEMO_STORY_SKIPPED=$(find "$dir" -type f \( -name "*.demo.tsx" -o -name "*.story.tsx" -o -name "*.stories.tsx" \) | wc -l) + + if [ "${{ github.event.inputs.sync_mode }}" = "existing-only" ]; then + # Count new files that were skipped + NEW_FILES_SKIPPED=0 + find "$dir" -type f ! -name "*.demo.tsx" ! -name "*.story.tsx" ! -name "*.stories.tsx" | while read -r file; do + target_file="$GITHUB_WORKSPACE/src/$file" + if [ ! -f "$target_file" ]; then + NEW_FILES_SKIPPED=$((NEW_FILES_SKIPPED + 1)) + fi + done + + if [ "$NEW_FILES_SKIPPED" -gt 0 ]; then + echo "Skipped $NEW_FILES_SKIPPED new files (existing-only mode)" + echo "- Skipped $NEW_FILES_SKIPPED new files (existing-only mode)" >> /tmp/sync-summary.md + fi + fi + + if [ "$DEMO_STORY_SKIPPED" -gt 0 ]; then + echo "Skipped $DEMO_STORY_SKIPPED demo/story files" + echo "- Skipped $DEMO_STORY_SKIPPED demo/story files" >> /tmp/sync-summary.md + fi + else + echo "Directory $dir not found in main repository" >> /tmp/sync-summary.md + fi + done + + - name: Check for changes + id: check-changes + run: | + cd $GITHUB_WORKSPACE + if git diff --quiet; then + echo "changes=false" >> $GITHUB_OUTPUT + echo "No changes detected" + else + echo "changes=true" >> $GITHUB_OUTPUT + echo "Changes detected" + fi + + - name: Generate detailed diff summary + if: steps.check-changes.outputs.changes == 'true' + run: | + cd $GITHUB_WORKSPACE + + echo "# Component sync changes" > /tmp/pr-description.md + echo "" >> /tmp/pr-description.md + echo "This PR synchronizes components from the main repository [untitleduico/react](https://github.com/untitleduico/react)." >> /tmp/pr-description.md + echo "" >> /tmp/pr-description.md + + # Add commits since last sync section + echo "## πŸ“ Commits since last sync" >> /tmp/pr-description.md + echo "" >> /tmp/pr-description.md + if [ -n "$LAST_SYNC_COMMIT" ] && [ "$COMMIT_COUNT" != "unknown" ]; then + if [ "$COMMIT_COUNT" -gt 0 ]; then + echo "**$COMMIT_COUNT new commits** from [\`$LAST_SYNC_COMMIT\`](https://github.com/untitleduico/react/commit/$LAST_SYNC_COMMIT) to [\`$LATEST_COMMIT_SHORT\`](https://github.com/untitleduico/react/commit/$LATEST_COMMIT_HASH):" >> /tmp/pr-description.md + echo "" >> /tmp/pr-description.md + echo "
" >> /tmp/pr-description.md + echo "View commit history" >> /tmp/pr-description.md + echo "" >> /tmp/pr-description.md + while IFS= read -r commit_line; do + if [ -n "$commit_line" ]; then + commit_hash=$(echo "$commit_line" | cut -d' ' -f1) + commit_msg=$(echo "$commit_line" | cut -d' ' -f2-) + echo "- [\`$commit_hash\`](https://github.com/untitleduico/react/commit/$commit_hash) $commit_msg" >> /tmp/pr-description.md + fi + done < /tmp/commits-since-sync.txt + echo "" >> /tmp/pr-description.md + echo "
" >> /tmp/pr-description.md + else + echo "βœ… **Repository is up to date** - no new commits since last sync [\`$LAST_SYNC_COMMIT\`](https://github.com/untitleduico/react/commit/$LAST_SYNC_COMMIT)" >> /tmp/pr-description.md + fi + else + echo "πŸŽ‰ **First sync** - syncing from latest commit [\`$LATEST_COMMIT_SHORT\`](https://github.com/untitleduico/react/commit/$LATEST_COMMIT_HASH)" >> /tmp/pr-description.md + echo "" >> /tmp/pr-description.md + echo "**Latest commit**: $LATEST_COMMIT_MESSAGE" >> /tmp/pr-description.md + echo "**Author**: $LATEST_COMMIT_AUTHOR" >> /tmp/pr-description.md + echo "**Date**: $LATEST_COMMIT_DATE" >> /tmp/pr-description.md + fi + echo "" >> /tmp/pr-description.md + + echo "## πŸ“ Modified files" >> /tmp/pr-description.md + echo "" >> /tmp/pr-description.md + + # Get file statistics + TOTAL_FILES=$(git diff --name-only | wc -l) + echo "**$TOTAL_FILES files modified**" >> /tmp/pr-description.md + echo "" >> /tmp/pr-description.md + + # List all changed files + git diff --name-only | while read -r file; do + if [ -n "$file" ]; then + echo "- \`$file\`" >> /tmp/pr-description.md + fi + done + echo "" >> /tmp/pr-description.md + + echo "## πŸ”§ Sync details" >> /tmp/pr-description.md + echo "" >> /tmp/pr-description.md + echo "- **Source repository**: [untitleduico/react](https://github.com/untitleduico/react)" >> /tmp/pr-description.md + echo "- **Latest commit**: [\`$LATEST_COMMIT_SHORT\`](https://github.com/untitleduico/react/commit/$LATEST_COMMIT_HASH)" >> /tmp/pr-description.md + echo "- **Sync date**: $(date -u '+%Y-%m-%d %H:%M:%S UTC')" >> /tmp/pr-description.md + echo "- **Directories synced**: ${{ github.event.inputs.sync_directories }}" >> /tmp/pr-description.md + echo "- **Sync mode**: ${{ github.event.inputs.sync_mode }}" >> /tmp/pr-description.md + echo "- **Automated processing**: Removed \`\"use client\";\` directives, excluded demo/story files" >> /tmp/pr-description.md + echo "" >> /tmp/pr-description.md + echo "---" >> /tmp/pr-description.md + echo "*This PR was automatically generated by the sync-components workflow.*" >> /tmp/pr-description.md + + - name: Store sync commit record + if: steps.check-changes.outputs.changes == 'true' + run: | + cd $GITHUB_WORKSPACE + + # Create .github directory if it doesn't exist + mkdir -p .github + + # Store the latest commit hash for next sync + echo "$LATEST_COMMIT_HASH" > .github/last-sync-commit + + echo "Stored sync commit: $LATEST_COMMIT_HASH" + + - name: Commit changes + if: steps.check-changes.outputs.changes == 'true' + run: | + cd $GITHUB_WORKSPACE + git add . + + # Create commit message + if [ -n "$LAST_SYNC_COMMIT" ] && [ "$COMMIT_COUNT" != "unknown" ] && [ "$COMMIT_COUNT" -gt 0 ]; then + COMMIT_MSG="sync: Update components from main repository ($COMMIT_COUNT commits) + + Synced from: $LAST_SYNC_COMMIT..$LATEST_COMMIT_SHORT + Latest commit: $LATEST_COMMIT_MESSAGE + Directories: ${{ github.event.inputs.sync_directories }} + Source: https://github.com/untitleduico/react/commit/$LATEST_COMMIT_HASH + Automated processing: Removed 'use client' directives" + else + COMMIT_MSG="sync: Initial sync from main repository + + Latest commit: $LATEST_COMMIT_SHORT - $LATEST_COMMIT_MESSAGE + Author: $LATEST_COMMIT_AUTHOR + Directories: ${{ github.event.inputs.sync_directories }} + Source: https://github.com/untitleduico/react/commit/$LATEST_COMMIT_HASH + Automated processing: Removed 'use client' directives" + fi + + git commit -m "$COMMIT_MSG" + + - name: Push changes and create PR + if: steps.check-changes.outputs.changes == 'true' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + cd $GITHUB_WORKSPACE + git push origin "$BRANCH_NAME" + + # Create PR title based on sync type + if [ -n "$LAST_SYNC_COMMIT" ] && [ "$COMMIT_COUNT" != "unknown" ] && [ "$COMMIT_COUNT" -gt 0 ]; then + PR_TITLE="πŸ”„ Sync components ($COMMIT_COUNT commits) β†’ $LATEST_COMMIT_SHORT" + else + PR_TITLE="πŸŽ‰ Initial sync from main repository β†’ $LATEST_COMMIT_SHORT" + fi + + # Create PR using GitHub CLI + gh pr create \ + --title "$PR_TITLE" \ + --body-file /tmp/pr-description.md \ + --head "$BRANCH_NAME" \ + --base main + + - name: Output results + run: | + if [ "${{ steps.check-changes.outputs.changes }}" == "true" ]; then + echo "βœ… Sync completed successfully. PR created." + echo "Branch: $BRANCH_NAME" + else + echo "ℹ️ No changes detected. Repository is already in sync." + fi diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7ceb59f --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +.env diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..16d51e6 --- /dev/null +++ b/.npmrc @@ -0,0 +1,3 @@ +@awesome.me:registry=https://npm.fontawesome.com/ +@fortawesome:registry=https://npm.fontawesome.com/ +//npm.fontawesome.com/:_authToken=B9FAA215-4936-4B13-BEAA-FA58F042FA1A diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..f4b9b99 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,19 @@ +{ + "printWidth": 160, + "tabWidth": 4, + "plugins": [ + "@trivago/prettier-plugin-sort-imports", + "prettier-plugin-tailwindcss" + ], + "tailwindFunctions": ["sortCx", "cx"], + "importOrder": [ + "^react$", + "^react-dom$", + "^(?!react$|react-dom$|@/|\\.).*", + "^@/.*", + "^\\.{1,2}/.*" + ], + "importOrderSeparation": false, + "importOrderSortSpecifiers": true, + "tailwindStylesheet": "./src/styles/globals.css" +} diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..721ccac --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,811 @@ +## Project Overview + +This is an **Untitled UI React** component library project built with: + +- **React 19.1.1** with TypeScript +- **Tailwind CSS v4.1** for styling +- **React Aria Components** as the foundation for accessibility and behavior + +## Key Architecture Principles + +### Component Foundation + +- All components are built on **React Aria Components** for consistent accessibility and behavior +- Components follow the compound component pattern with sub-components (e.g., `Select.Item`, `Select.ComboBox`) +- TypeScript is used throughout for type safety + +### Import Naming Convention + +**CRITICAL**: All imports from `react-aria-components` must be prefixed with `Aria*` for clarity and consistency: + +```typescript +// βœ… Correct +import { Button as AriaButton, TextField as AriaTextField } from "react-aria-components"; +// ❌ Incorrect +import { Button, TextField } from "react-aria-components"; +``` + +This convention: + +- Prevents naming conflicts with custom components +- Makes it clear when using base React Aria components +- Maintains consistency across the entire codebase + +### File Naming Convention + +**IMPORTANT**: All files must be named in **kebab-case** for consistency: + +``` +βœ… Correct: +- date-picker.tsx +- user-profile.tsx +- api-client.ts +- auth-context.tsx + +❌ Incorrect: +- DatePicker.tsx +- userProfile.tsx +- apiClient.ts +- AuthContext.tsx +``` + +This applies to all file types including: + +- Component files (.tsx, .jsx) +- TypeScript/JavaScript files (.ts, .js) +- Style files (.css, .scss) +- Test files (.test.ts, .spec.tsx) +- Configuration files (when creating new ones) + +## Development Commands + +```bash +# Development +npm run dev # Start Vite development server (http://localhost:5173) +npm run build # Build for production (TypeScript compilation + Vite build) +``` + +## Project Structure + +### Application Architecture + +``` +src/ +β”œβ”€β”€ components/ +β”‚ β”œβ”€β”€ base/ # Core UI components (Button, Input, Select, etc.) +β”‚ β”œβ”€β”€ application/ # Complex application components +β”‚ β”œβ”€β”€ foundations/ # Design tokens and foundational elements +β”‚ β”œβ”€β”€ marketing/ # Marketing-specific components +β”‚ └── shared-assets/ # Reusable assets and illustrations +β”œβ”€β”€ hooks/ # Custom React hooks +β”œβ”€β”€ pages/ # Route components +β”œβ”€β”€ providers/ # React context providers +β”œβ”€β”€ styles/ # Global styles and theme +β”œβ”€β”€ types/ # TypeScript type definitions +└── utils/ # Utility functions +``` + +### Component Patterns + +#### 1. Base Components + +Located in `components/base/`, these are the building blocks: + +- `Button` - All button variants with loading states +- `Input` - Text inputs with validation and icons +- `Select` - Dropdown selections with complex options +- `Checkbox`, `Radio`, `Toggle` - Form controls +- `Avatar`, `Badge`, `Tooltip` - Display components + +#### 2. Application Components + +Located in `components/application/`, these are complex UI patterns: + +- `DatePicker` - Calendar-based date selection +- `Modal` - Overlay dialogs +- `Pagination` - Data navigation +- `Table` - Data display with sorting +- `Tabs` - Content organization + +#### 3. Styling Architecture + +- Uses a `sortCx` utility for organized style objects +- Follows size variants: `sm`, `md`, `lg`, `xl` +- Color variants: `primary`, `secondary`, `tertiary`, `destructive`, etc. +- Responsive and state-aware styling with Tailwind + +#### 4. Component Props Pattern + +```typescript +interface CommonProps { + size?: "sm" | "md" | "lg"; + isDisabled?: boolean; + isLoading?: boolean; + // ... other common props +} + +interface ButtonProps extends CommonProps, HTMLButtonElement { + color?: "primary" | "secondary" | "tertiary"; + iconLeading?: FC | ReactNode; + iconTrailing?: FC | ReactNode; +} +``` + +## Styling Guidelines + +### Tailwind CSS v4.1 + +- Uses the latest Tailwind CSS v4.1 features +- Custom design tokens defined in theme configuration +- Consistent spacing, colors, and typography scales + +### Brand Color Customization + +To change the main brand color across the entire application: + +1. **Update Brand Color Variables**: Edit `src/styles/theme.css` and modify the `--color-brand-*` variables +2. **Maintain Color Scale**: Ensure you provide a complete color scale from 25 to 950 with proper contrast ratios +3. **Example Brand Color Scale**: + ```css + --color-brand-25: rgb(252 250 255); /* Lightest tint */ + --color-brand-50: rgb(249 245 255); + --color-brand-100: rgb(244 235 255); + --color-brand-200: rgb(233 215 254); + --color-brand-300: rgb(214 187 251); + --color-brand-400: rgb(182 146 246); + --color-brand-500: rgb(158 119 237); /* Base brand color */ + --color-brand-600: rgb(127 86 217); /* Primary interactive color */ + --color-brand-700: rgb(105 65 198); + --color-brand-800: rgb(83 56 158); + --color-brand-900: rgb(66 48 125); + --color-brand-950: rgb(44 28 95); /* Darkest shade */ + ``` + +The color scale automatically adapts to both light and dark modes through the CSS variable system. + +### Style Organization + +```typescript +export const styles = sortCx({ + common: { + root: "base-classes-here", + icon: "icon-classes-here", + }, + sizes: { + sm: { root: "small-size-classes" }, + md: { root: "medium-size-classes" }, + }, + colors: { + primary: { root: "primary-color-classes" }, + secondary: { root: "secondary-color-classes" }, + }, +}); +``` + +### Utility Functions + +- `cx()` - Class name utility (from `@/utils/cx`) +- `sortCx()` - Organized style objects +- `isReactComponent()` - Component type checking + +## Icon Usage + +### Available Libraries + +- `@untitledui/icons` - 1,100+ line-style icons (free) +- `@untitledui/file-icons` - File type icons +- `@untitledui-pro/icons` - 4,600+ icons in 4 styles (Requires PRO access) + +### Import & Usage + +```typescript +// Recommended: Named imports (tree-shakeable) +import { Home01, Settings01, ChevronDown } from "@untitledui/icons"; + +// Component props - pass as reference + + +// Standalone usage + + +// As JSX element - MUST include data-icon + +``` + +### Styling + +```typescript +// Size: use size-4 (16px), size-5 (20px), size-6 (24px) + + +// Color: use semantic text colors + + +// Stroke width (line icons only) + + +// Accessibility: decorative icons need aria-hidden +