Wip on homepage.
Add documentation how to optimize code size in production.
This commit is contained in:
157
docs/application-optimization.md
Normal file
157
docs/application-optimization.md
Normal file
@@ -0,0 +1,157 @@
|
||||
# Application.js Size Optimization Guide
|
||||
|
||||
## Current Issue
|
||||
The `application.js` bundle is 1.4MB (2.3MB with source maps), which is significantly larger than recommended.
|
||||
|
||||
## Root Causes
|
||||
1. **Single bundle includes everything**: All dependencies, React, controllers, and components
|
||||
2. **No code splitting**: Everything is bundled into one file
|
||||
3. **Development dependencies**: Alpine.js and other dev tools included
|
||||
4. **No minification/optimization**: Source maps and uncompressed code
|
||||
|
||||
## Optimization Strategies
|
||||
|
||||
### 1. Split Bundles (Recommended)
|
||||
Create separate bundles for different parts of the application:
|
||||
|
||||
**Update package.json build scripts:**
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"build": "npm run build:main && npm run build:components",
|
||||
"build:main": "esbuild app/javascript/application.js --bundle --minify --sourcemap --format=esm --outdir=app/assets/builds --public-path=/assets",
|
||||
"build:components": "esbuild app/javascript/components/*.* --bundle --minify --format=esm --outdir=app/assets/builds/components --public-path=/assets --loader:.js=jsx",
|
||||
"build:css": "postcss ./app/assets/stylesheets/application.postcss.css -o ./app/assets/builds/application.css"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Remove Unused Dependencies
|
||||
**package.json optimization:**
|
||||
```json
|
||||
{
|
||||
"dependencies": {
|
||||
"@hotwired/stimulus": "^3.2.2",
|
||||
"@hotwired/turbo-rails": "^8.0.13",
|
||||
"@radix-ui/react-slot": "^1.2.3",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.1.4",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"esbuild": "^0.25.4",
|
||||
"postcss": "^8.5.3",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tailwindcss": "^4.1.4",
|
||||
"tailwindcss-animate": "^1.0.7"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Remove these from devDependencies:**
|
||||
- `alpinejs` - if not used
|
||||
- `@types/alpinejs` - if Alpine.js removed
|
||||
- `cssnano` - if using Tailwind's built-in minification
|
||||
- `pm2` - production deployment tool
|
||||
|
||||
### 3. Dynamic Imports (Code Splitting)
|
||||
**Update application.js:**
|
||||
```javascript
|
||||
// Instead of importing everything statically
|
||||
import "@hotwired/turbo-rails"
|
||||
import "./controllers"
|
||||
|
||||
// Use dynamic imports for heavy components
|
||||
const loadComponent = async (componentName) => {
|
||||
const { default: component } = await import(`./components/${componentName}`)
|
||||
return component
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Tree Shaking & Minification
|
||||
**Enhanced build command:**
|
||||
```json
|
||||
{
|
||||
"build": "esbuild app/javascript/application.js --bundle --minify --tree-shaking --drop:console --drop:debugger --sourcemap=external --format=esm --outdir=app/assets/builds --public-path=/assets"
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Separate Vendor Bundle
|
||||
**Create vendor.js:**
|
||||
```javascript
|
||||
// app/javascript/vendor.js
|
||||
import "react"
|
||||
import "react-dom"
|
||||
import "@radix-ui/react-slot"
|
||||
```
|
||||
|
||||
**Update build to create vendor bundle:**
|
||||
```json
|
||||
{
|
||||
"build:vendor": "esbuild app/javascript/vendor.js --bundle --minify --format=esm --outdir=app/assets/builds --public-path=/assets",
|
||||
"build:app": "esbuild app/javascript/application.js --bundle --minify --external:react --external:react-dom --format=esm --outdir=app/assets/builds --public-path=/assets"
|
||||
}
|
||||
```
|
||||
|
||||
### 6. Conditional Loading
|
||||
**Lazy load heavy components:**
|
||||
```javascript
|
||||
// app/javascript/application.js
|
||||
if (document.querySelector('[data-controller="shadcn-test"]')) {
|
||||
import('./controllers/shadcn_test_controller')
|
||||
}
|
||||
```
|
||||
|
||||
### 7. Production Optimization Checklist
|
||||
|
||||
**Step 1: Analyze bundle size**
|
||||
```bash
|
||||
npm install --save-dev webpack-bundle-analyzer
|
||||
npx esbuild app/javascript/application.js --bundle --analyze
|
||||
```
|
||||
|
||||
**Step 2: Implement optimizations**
|
||||
```bash
|
||||
# Remove unused dependencies
|
||||
npm uninstall alpinejs @types/alpinejs cssnano pm2
|
||||
|
||||
# Update build scripts
|
||||
npm run build
|
||||
```
|
||||
|
||||
**Step 3: Verify size reduction**
|
||||
Should reduce from 1.4MB to ~200-400KB
|
||||
|
||||
## Quick Fix Commands
|
||||
|
||||
```bash
|
||||
# 1. Remove Alpine.js (if unused)
|
||||
npm uninstall alpinejs @types/alpinejs
|
||||
|
||||
# 2. Update build with optimization
|
||||
npm install --save-dev esbuild@latest
|
||||
|
||||
# 3. Modify package.json scripts
|
||||
# (Copy the optimized scripts above)
|
||||
|
||||
# 4. Build with optimization
|
||||
npm run build
|
||||
```
|
||||
|
||||
## Expected Results
|
||||
- **Before**: 1.4MB application.js
|
||||
- **After**: 200-400KB with code splitting
|
||||
- **Vendor bundle**: ~100KB (cached)
|
||||
- **App bundle**: ~100-300KB (dynamic)
|
||||
|
||||
## Monitoring
|
||||
Add bundle size monitoring to CI/CD:
|
||||
```json
|
||||
{
|
||||
"size-limits": {
|
||||
"app/assets/builds/application.js": "500kb",
|
||||
"app/assets/builds/application.css": "50kb"
|
||||
}
|
||||
}
|
||||
288
docs/creating-shadcn-react-components.md
Normal file
288
docs/creating-shadcn-react-components.md
Normal file
@@ -0,0 +1,288 @@
|
||||
# Creating New Shadcn and React Components
|
||||
|
||||
This guide explains how to create new Shadcn (UI) components and React components in this Rails application with React frontend.
|
||||
|
||||
## Overview
|
||||
|
||||
This project uses:
|
||||
- **Shadcn/ui** for UI components (built on Radix UI and Tailwind CSS)
|
||||
- **React** for frontend components
|
||||
- **Rails** as the backend framework
|
||||
- **esbuild** for JavaScript bundling
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
app/
|
||||
├── javascript/
|
||||
│ ├── components/
|
||||
│ │ └── ui/ # Shadcn components
|
||||
│ └── controllers/ # React controllers
|
||||
├── views/
|
||||
│ └── components/ # Rails view components
|
||||
└── docs/ # Documentation
|
||||
```
|
||||
|
||||
## Creating Shadcn Components
|
||||
|
||||
### 1. Using the Shadcn CLI
|
||||
|
||||
The easiest way to add new Shadcn components is using the CLI:
|
||||
|
||||
```bash
|
||||
# Navigate to the project root
|
||||
cd /home/acid/Documents/aperonight
|
||||
|
||||
# Add a new component (example: adding a card)
|
||||
npx shadcn-ui@latest add card
|
||||
```
|
||||
|
||||
This will:
|
||||
- Install the component to `app/javascript/components/ui/`
|
||||
- Update the components.json configuration
|
||||
- Create the necessary TypeScript/JavaScript files
|
||||
|
||||
### 2. Manual Component Creation
|
||||
|
||||
If the CLI is not available, create components manually:
|
||||
|
||||
#### Create the component file
|
||||
```bash
|
||||
# Create a new component (example: button.jsx)
|
||||
touch app/javascript/components/ui/button.jsx
|
||||
```
|
||||
|
||||
#### Basic component structure
|
||||
```javascript
|
||||
// app/javascript/components/ui/button.jsx
|
||||
import * as React from "react"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const Button = React.forwardRef(({ className, ...props }, ref) => {
|
||||
return (
|
||||
<button
|
||||
className={cn(
|
||||
"inline-flex items-center justify-center rounded-md text-sm font-medium",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
})
|
||||
Button.displayName = "Button"
|
||||
|
||||
export { Button }
|
||||
```
|
||||
|
||||
## Creating React Components
|
||||
|
||||
### 1. Controller-Based Components
|
||||
|
||||
For components that need Rails integration:
|
||||
|
||||
#### Create controller file
|
||||
```bash
|
||||
# Create a new controller
|
||||
touch app/javascript/controllers/my_component_controller.js
|
||||
```
|
||||
|
||||
#### Basic controller structure
|
||||
```javascript
|
||||
// app/javascript/controllers/my_component_controller.js
|
||||
import { Controller } from "@hotwired/stimulus"
|
||||
import React from "react"
|
||||
import ReactDOM from "react-dom/client"
|
||||
|
||||
export default class extends Controller {
|
||||
static targets = ["container"]
|
||||
|
||||
connect() {
|
||||
const root = ReactDOM.createRoot(this.containerTarget)
|
||||
root.render(<MyComponent />)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Standalone React Components
|
||||
|
||||
For reusable React components:
|
||||
|
||||
#### Create component file
|
||||
```bash
|
||||
# Create a new React component
|
||||
touch app/javascript/components/MyNewComponent.jsx
|
||||
```
|
||||
|
||||
#### Component structure
|
||||
```javascript
|
||||
// app/javascript/components/MyNewComponent.jsx
|
||||
import React from "react"
|
||||
|
||||
const MyNewComponent = ({ title, description }) => {
|
||||
return (
|
||||
<div className="p-4 border rounded-lg">
|
||||
<h2 className="text-lg font-semibold">{title}</h2>
|
||||
<p className="text-gray-600">{description}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default MyNewComponent
|
||||
```
|
||||
|
||||
## Integration Patterns
|
||||
|
||||
### 1. Using in Rails Views
|
||||
|
||||
To use components in Rails views:
|
||||
|
||||
#### Create partial
|
||||
```erb
|
||||
<!-- app/views/components/_my_component.html.erb -->
|
||||
<div data-controller="my-component">
|
||||
<div data-my-component-target="container"></div>
|
||||
</div>
|
||||
```
|
||||
|
||||
#### Include in page
|
||||
```erb
|
||||
<!-- app/views/pages/home.html.erb -->
|
||||
<%= render "components/my_component" %>
|
||||
```
|
||||
|
||||
### 2. Direct React Rendering
|
||||
|
||||
For pages that are primarily React:
|
||||
|
||||
#### Create page component
|
||||
```javascript
|
||||
// app/javascript/components/pages/HomePage.jsx
|
||||
import React from "react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
|
||||
const HomePage = () => {
|
||||
return (
|
||||
<div className="container mx-auto">
|
||||
<h1>Welcome</h1>
|
||||
<Button>Get Started</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default HomePage
|
||||
```
|
||||
|
||||
## Configuration Updates
|
||||
|
||||
### 1. Update components.json
|
||||
```json
|
||||
{
|
||||
"style": "default",
|
||||
"rsc": false,
|
||||
"tsx": false,
|
||||
"tailwind": {
|
||||
"config": "tailwind.config.js",
|
||||
"css": "app/assets/stylesheets/application.postcss.css",
|
||||
"baseColor": "slate",
|
||||
"cssVariables": true
|
||||
},
|
||||
"aliases": {
|
||||
"components": "app/javascript/components",
|
||||
"utils": "app/javascript/lib/utils"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Update JavaScript entry point
|
||||
```javascript
|
||||
// app/javascript/application.js
|
||||
import "./components"
|
||||
import "./controllers"
|
||||
```
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
### Shadcn Components
|
||||
- Use kebab-case for filenames: `button.jsx`, `card.jsx`
|
||||
- Use PascalCase for exports: `export { Button }`
|
||||
- Follow Radix UI naming patterns
|
||||
|
||||
### React Components
|
||||
- Use PascalCase for filenames: `MyComponent.jsx`
|
||||
- Use PascalCase for components: `const MyComponent = () => {}`
|
||||
- Use camelCase for props: `myProp`, `onClick`
|
||||
|
||||
## Testing Components
|
||||
|
||||
### 1. Create test file
|
||||
```bash
|
||||
# Create test file
|
||||
touch test/components/my_component_test.rb
|
||||
```
|
||||
|
||||
### 2. Write component test
|
||||
```javascript
|
||||
// test/components/my_component_test.jsx
|
||||
import { render, screen } from "@testing-library/react"
|
||||
import MyComponent from "../../app/javascript/components/MyComponent"
|
||||
|
||||
test("renders component", () => {
|
||||
render(<MyComponent title="Test" />)
|
||||
expect(screen.getByText("Test")).toBeInTheDocument()
|
||||
})
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### 1. Props Pattern
|
||||
```javascript
|
||||
// Pass Rails data as props
|
||||
const MyComponent = ({ user, config }) => {
|
||||
return <div>{user.name}</div>
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Event Handling
|
||||
```javascript
|
||||
// Handle events from Rails
|
||||
const MyComponent = ({ onAction }) => {
|
||||
return <button onClick={onAction}>Click me</button>
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Styling Integration
|
||||
```javascript
|
||||
// Use Tailwind classes
|
||||
const MyComponent = () => {
|
||||
return <div className="bg-white dark:bg-gray-800">Content</div>
|
||||
}
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **Component not rendering**: Check controller connection
|
||||
2. **Styling issues**: Verify Tailwind classes
|
||||
3. **Props not passing**: Check data-controller attributes
|
||||
4. **Import errors**: Verify alias paths in components.json
|
||||
|
||||
### Debug Steps
|
||||
|
||||
1. Check browser console for errors
|
||||
2. Verify component file exists in correct location
|
||||
3. Check import paths in application.js
|
||||
4. Verify Rails view includes correct data attributes
|
||||
|
||||
|
||||
## Example created for testing purpose
|
||||
|
||||
```html
|
||||
<!-- Shadcn Button Test -->
|
||||
<div data-controller="shadcn-test" class="mt-4">
|
||||
<div data-shadcn-test-target="container"></div>
|
||||
</div>
|
||||
```
|
||||
|
||||
|
||||
103
docs/theme-rules.md
Normal file
103
docs/theme-rules.md
Normal file
@@ -0,0 +1,103 @@
|
||||
# Theme Rules & Color Palette - Aperonight
|
||||
|
||||
Extracted from `app/views/pages/home.html.erb`
|
||||
|
||||
## Color Palette
|
||||
|
||||
### Primary Colors
|
||||
- **Indigo**: `#4338ca` (rgb(67, 56, 202)) - Used in hero gradient
|
||||
- **Purple**: `#8b5cf6` (rgb(139, 92, 246)) - Primary brand color
|
||||
- **Pink**: `#ec4899` (rgb(236, 72, 153)) - Accent color
|
||||
|
||||
### Background Gradients
|
||||
- **Hero**: `bg-gradient-to-br from-indigo-900 via-purple-800 to-pink-700`
|
||||
- **CTA**: `bg-gradient-to-r from-purple-900 via-indigo-900 to-pink-900`
|
||||
- **Cards**: `bg-gradient-to-br from-gray-800 to-gray-900`
|
||||
- **Buttons**: `bg-gradient-to-r from-purple-600 to-pink-600`
|
||||
|
||||
### Text Colors
|
||||
- **White**: `text-white` - Primary text
|
||||
- **Gray-200**: `text-gray-200` - Secondary text
|
||||
- **Gray-300**: `text-gray-300` - Subtle text
|
||||
- **Gray-400**: `text-gray-400` - Muted text
|
||||
- **Transparent gradient**: `text-transparent bg-clip-text bg-gradient-to-r from-purple-400 to-pink-400` - Special highlight
|
||||
|
||||
### Background Colors
|
||||
- **Gray-900**: `bg-gray-900` - Main background
|
||||
- **Black**: `bg-black` - Overlay backgrounds
|
||||
- **Gray-800**: `bg-gray-800` - Card backgrounds
|
||||
- **White/Transparent**: `bg-white bg-opacity-10 backdrop-blur-sm` - Glass effect
|
||||
|
||||
## Spacing & Layout
|
||||
|
||||
### Hero Section
|
||||
- **Height**: `min-h-[70vh]`
|
||||
- **Max-width**: `max-w-7xl mx-auto`
|
||||
- **Padding**: `px-4 sm:px-6 lg:px-8`
|
||||
|
||||
### Grid Layouts
|
||||
- **Responsive**: `grid-cols-1 md:grid-cols-2 lg:grid-cols-3`
|
||||
- **Gap**: `gap-8` standard spacing
|
||||
|
||||
### Padding Classes
|
||||
- **Section**: `py-16`, `py-20`
|
||||
- **Card**: `p-4`, `p-6`, `p-8`
|
||||
- **Button**: `py-3`, `py-4`, `px-6`, `px-8`
|
||||
|
||||
## Typography
|
||||
|
||||
### Font Sizes
|
||||
- **Hero Title**: `text-5xl md:text-7xl`
|
||||
- **Section Title**: `text-4xl`
|
||||
- **Card Title**: `text-2xl`
|
||||
- **Body**: `text-xl`, `text-lg`
|
||||
- **Small**: `text-sm`
|
||||
|
||||
### Font Weights
|
||||
- **Bold**: `font-bold` (headings)
|
||||
- **Semibold**: `font-semibold` (buttons, important text)
|
||||
- **Medium**: `font-medium` (labels)
|
||||
|
||||
## Interactive States
|
||||
|
||||
### Hover Effects
|
||||
- **Scale**: `hover:scale-105`
|
||||
- **Transition**: `transition-all duration-300`
|
||||
- **Button Hover**: `hover:from-purple-700 hover:to-pink-700`
|
||||
- **Glass Hover**: `hover:bg-opacity-20`
|
||||
|
||||
### Shadows
|
||||
- **Default**: `shadow-lg`
|
||||
- **Strong**: `shadow-xl`
|
||||
- **Card**: `shadow-2xl`
|
||||
|
||||
## Border Radius
|
||||
- **Buttons**: `rounded-full` (pill-shaped)
|
||||
- **Cards**: `rounded-2xl`
|
||||
- **Inputs**: `rounded-lg`
|
||||
|
||||
## Icon Colors
|
||||
- **Primary**: `text-white` (on colored backgrounds)
|
||||
- **Accent**: `text-purple-400`, `text-pink-400`
|
||||
- **Muted**: `text-gray-400`
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Primary Button
|
||||
```html
|
||||
class="bg-gradient-to-r from-purple-600 to-pink-600 hover:from-purple-700 hover:to-pink-700 text-white font-semibold py-4 px-8 rounded-full transition-all duration-300 transform hover:scale-105 shadow-lg"
|
||||
```
|
||||
|
||||
### Card Background
|
||||
```html
|
||||
class="bg-gradient-to-br from-gray-800 to-gray-900 rounded-2xl overflow-hidden hover:transform hover:scale-105 transition-all duration-300 shadow-xl"
|
||||
```
|
||||
|
||||
### Hero Gradient
|
||||
```html
|
||||
class="bg-gradient-to-br from-indigo-900 via-purple-800 to-pink-700"
|
||||
```
|
||||
|
||||
### Glass Effect
|
||||
```html
|
||||
class="bg-white bg-opacity-10 backdrop-blur-sm border border-white border-opacity-30"
|
||||
Reference in New Issue
Block a user