create-template-html-css 2.0.3 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +305 -0
- package/HTML-VS-REACT.md +289 -0
- package/QUICKSTART-REACT.md +293 -0
- package/REACT-SUPPORT-SUMMARY.md +235 -0
- package/README.md +193 -12
- package/bin/cli.js +98 -759
- package/bin/commands/create.js +272 -0
- package/bin/commands/gallery.js +42 -0
- package/bin/commands/insert.js +123 -0
- package/bin/commands/list.js +73 -0
- package/package.json +10 -3
- package/src/component-choices.js +7 -0
- package/src/components-registry.js +112 -0
- package/src/format-utils.js +49 -0
- package/src/generator.js +83 -594
- package/src/generators/color-schemes.js +78 -0
- package/src/generators/color-utils.js +108 -0
- package/src/generators/component-filters.js +151 -0
- package/src/generators/html-generators.js +180 -0
- package/src/generators/validation.js +43 -0
- package/src/index.js +2 -1
- package/src/inserter.js +55 -233
- package/src/inserters/backup-utils.js +20 -0
- package/src/inserters/component-loader.js +68 -0
- package/src/inserters/html-utils.js +31 -0
- package/src/inserters/indentation-utils.js +90 -0
- package/src/inserters/validation-utils.js +49 -0
- package/src/react-component-choices.js +45 -0
- package/src/react-file-operations.js +172 -0
- package/src/react-generator.js +208 -0
- package/src/react-templates.js +350 -0
- package/src/utils/file-utils.js +97 -0
- package/src/utils/path-utils.js +32 -0
- package/src/utils/string-utils.js +51 -0
- package/src/utils/template-loader.js +91 -0
- package/templates/_shared/PATTERNS.md +246 -0
- package/templates/_shared/README.md +74 -0
- package/templates/_shared/base.css +18 -0
- package/templates/blackjack/index.html +1 -1
- package/templates/blackjack/script.js +9 -9
- package/templates/breakout/index.html +1 -1
- package/templates/breakout/script.js +6 -6
- package/templates/connect-four/index.html +1 -1
- package/templates/connect-four/script.js +5 -5
- package/templates/dice-game/index.html +1 -1
- package/templates/dice-game/script.js +20 -20
- package/templates/flappy-bird/index.html +1 -1
- package/templates/flappy-bird/script.js +10 -10
- package/templates/pong/index.html +1 -1
- package/templates/pong/script.js +8 -8
- package/templates/skeleton/index.html +4 -4
- package/templates/slot-machine/index.html +1 -1
- package/templates/slot-machine/script.js +6 -6
- package/templates/tetris/index.html +1 -1
- package/templates/tetris/script.js +5 -5
- package/templates-react/README.md +126 -0
- package/templates-react/button/Button.css +88 -0
- package/templates-react/button/Button.example.jsx +40 -0
- package/templates-react/button/Button.jsx +29 -0
- package/templates-react/card/Card.css +86 -0
- package/templates-react/card/Card.example.jsx +49 -0
- package/templates-react/card/Card.jsx +35 -0
- package/templates-react/counter/Counter.css +99 -0
- package/templates-react/counter/Counter.example.jsx +45 -0
- package/templates-react/counter/Counter.jsx +70 -0
- package/templates-react/form/Form.css +128 -0
- package/templates-react/form/Form.example.jsx +65 -0
- package/templates-react/form/Form.jsx +125 -0
- package/templates-react/modal/Modal.css +152 -0
- package/templates-react/modal/Modal.example.jsx +90 -0
- package/templates-react/modal/Modal.jsx +46 -0
- package/templates-react/todo-list/TodoList.css +236 -0
- package/templates-react/todo-list/TodoList.example.jsx +15 -0
- package/templates-react/todo-list/TodoList.jsx +84 -0
|
@@ -279,15 +279,15 @@ function gameOver() {
|
|
|
279
279
|
ctx.fillText('Game Over!', canvas.width / 2, canvas.height / 2 - 50);
|
|
280
280
|
|
|
281
281
|
ctx.font = '24px Arial';
|
|
282
|
-
ctx.strokeText(
|
|
283
|
-
ctx.fillText(
|
|
282
|
+
ctx.strokeText(`Score: ${gameState.score}`, canvas.width / 2, canvas.height / 2 + 10);
|
|
283
|
+
ctx.fillText(`Score: ${gameState.score}`, canvas.width / 2, canvas.height / 2 + 10);
|
|
284
284
|
|
|
285
|
-
ctx.strokeText(
|
|
286
|
-
ctx.fillText(
|
|
285
|
+
ctx.strokeText(`High Score: ${gameState.highScore}`, canvas.width / 2, canvas.height / 2 + 50);
|
|
286
|
+
ctx.fillText(`High Score: ${gameState.highScore}`, canvas.width / 2, canvas.height / 2 + 50);
|
|
287
287
|
|
|
288
288
|
ctx.font = '18px Arial';
|
|
289
|
-
ctx.strokeText('
|
|
290
|
-
ctx.fillText('
|
|
289
|
+
ctx.strokeText('Click "Reset" to restart', canvas.width / 2, canvas.height / 2 + 100);
|
|
290
|
+
ctx.fillText('Click "Reset" to restart', canvas.width / 2, canvas.height / 2 + 100);
|
|
291
291
|
|
|
292
292
|
document.getElementById('startBtn').disabled = false;
|
|
293
293
|
}
|
|
@@ -318,11 +318,11 @@ function drawStartScreen() {
|
|
|
318
318
|
ctx.fillText('🐦 Flappy Bird', canvas.width / 2, canvas.height / 2 - 40);
|
|
319
319
|
|
|
320
320
|
ctx.font = '20px Arial';
|
|
321
|
-
ctx.strokeText('
|
|
322
|
-
ctx.fillText('
|
|
321
|
+
ctx.strokeText('Click "Start Game"', canvas.width / 2, canvas.height / 2 + 20);
|
|
322
|
+
ctx.fillText('Click "Start Game"', canvas.width / 2, canvas.height / 2 + 20);
|
|
323
323
|
|
|
324
|
-
ctx.strokeText('
|
|
325
|
-
ctx.fillText('
|
|
324
|
+
ctx.strokeText('to begin!', canvas.width / 2, canvas.height / 2 + 50);
|
|
325
|
+
ctx.fillText('to begin!', canvas.width / 2, canvas.height / 2 + 50);
|
|
326
326
|
}
|
|
327
327
|
|
|
328
328
|
// Game loop
|
package/templates/pong/script.js
CHANGED
|
@@ -89,10 +89,10 @@ document.querySelectorAll('.mode-btn').forEach(btn => {
|
|
|
89
89
|
|
|
90
90
|
// Update player 2 name
|
|
91
91
|
if (gameState.gameMode === 'pvc') {
|
|
92
|
-
document.getElementById('player2Name').textContent = '🤖
|
|
92
|
+
document.getElementById('player2Name').textContent = '🤖 Computer';
|
|
93
93
|
document.querySelector('.difficulty-selector').style.display = 'flex';
|
|
94
94
|
} else {
|
|
95
|
-
document.getElementById('player2Name').textContent = '👤
|
|
95
|
+
document.getElementById('player2Name').textContent = '👤 Player 2';
|
|
96
96
|
document.querySelector('.difficulty-selector').style.display = 'none';
|
|
97
97
|
}
|
|
98
98
|
});
|
|
@@ -251,7 +251,7 @@ function draw() {
|
|
|
251
251
|
ctx.fillStyle = '#FFFFFF';
|
|
252
252
|
ctx.font = 'bold 36px Arial';
|
|
253
253
|
ctx.textAlign = 'center';
|
|
254
|
-
ctx.fillText('
|
|
254
|
+
ctx.fillText('Game Paused', canvas.width / 2, canvas.height / 2);
|
|
255
255
|
}
|
|
256
256
|
}
|
|
257
257
|
|
|
@@ -288,10 +288,10 @@ function endGame(winner) {
|
|
|
288
288
|
gameState.running = false;
|
|
289
289
|
|
|
290
290
|
const winnerName = winner === 1 ?
|
|
291
|
-
'
|
|
292
|
-
(gameState.gameMode === 'pvc' ? '
|
|
291
|
+
'Player 1' :
|
|
292
|
+
(gameState.gameMode === 'pvc' ? 'Computer' : 'Player 2');
|
|
293
293
|
|
|
294
|
-
showMessage(`🎉 ${winnerName}
|
|
294
|
+
showMessage(`🎉 ${winnerName} wins ${gameState.winningScore} - ${winner === 1 ? gameState.score2 : gameState.score1}!`, 'success');
|
|
295
295
|
|
|
296
296
|
document.getElementById('startBtn').disabled = false;
|
|
297
297
|
document.getElementById('pauseBtn').disabled = true;
|
|
@@ -325,7 +325,7 @@ document.getElementById('pauseBtn').addEventListener('click', pauseGame);
|
|
|
325
325
|
|
|
326
326
|
function pauseGame() {
|
|
327
327
|
gameState.paused = !gameState.paused;
|
|
328
|
-
document.getElementById('pauseBtn').textContent = gameState.paused ? '
|
|
328
|
+
document.getElementById('pauseBtn').textContent = gameState.paused ? 'Resume' : 'Pause';
|
|
329
329
|
}
|
|
330
330
|
|
|
331
331
|
// Reset game
|
|
@@ -347,7 +347,7 @@ function resetGame() {
|
|
|
347
347
|
|
|
348
348
|
document.getElementById('startBtn').disabled = false;
|
|
349
349
|
document.getElementById('pauseBtn').disabled = true;
|
|
350
|
-
document.getElementById('pauseBtn').textContent = '
|
|
350
|
+
document.getElementById('pauseBtn').textContent = 'Pause';
|
|
351
351
|
document.getElementById('resultMessage').textContent = '';
|
|
352
352
|
}
|
|
353
353
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
|
-
<meta charset="UTF-8"
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0"
|
|
6
|
-
<title>Skeleton Loading</title>
|
|
7
|
-
<link rel="stylesheet" href="style.css"
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>{{name}} - Skeleton Loading</title>
|
|
7
|
+
<link rel="stylesheet" href="style.css">
|
|
8
8
|
</head>
|
|
9
9
|
<body>
|
|
10
10
|
<div class="skeleton-container">
|
|
@@ -72,7 +72,7 @@ async function spin() {
|
|
|
72
72
|
if (gameState.spinning) return;
|
|
73
73
|
|
|
74
74
|
if (gameState.credits < gameState.bet) {
|
|
75
|
-
showMessage('
|
|
75
|
+
showMessage('Not enough credits!', 'error');
|
|
76
76
|
return;
|
|
77
77
|
}
|
|
78
78
|
|
|
@@ -105,7 +105,7 @@ async function spin() {
|
|
|
105
105
|
// Check if out of credits
|
|
106
106
|
if (gameState.credits < 10) {
|
|
107
107
|
setTimeout(() => {
|
|
108
|
-
alert('
|
|
108
|
+
alert('Out of credits! Receiving 1000 new credits.');
|
|
109
109
|
gameState.credits = 1000;
|
|
110
110
|
updateDisplay();
|
|
111
111
|
saveGame();
|
|
@@ -171,18 +171,18 @@ function checkWin(results) {
|
|
|
171
171
|
let type = 'success';
|
|
172
172
|
|
|
173
173
|
if (first.icon === '💰') {
|
|
174
|
-
message = `🎉
|
|
174
|
+
message = `🎉 Jackpot! You won ${winAmount} credits! 🎉`;
|
|
175
175
|
type = 'jackpot';
|
|
176
176
|
celebrate();
|
|
177
177
|
} else {
|
|
178
|
-
message = `🎊
|
|
178
|
+
message = `🎊 You Win! +${winAmount} credits!`;
|
|
179
179
|
}
|
|
180
180
|
|
|
181
181
|
showMessage(message, type);
|
|
182
182
|
} else if (first.icon === second.icon || second.icon === third.icon || first.icon === third.icon) {
|
|
183
|
-
showMessage('
|
|
183
|
+
showMessage('Almost! Two matching symbols!', 'info');
|
|
184
184
|
} else {
|
|
185
|
-
showMessage('
|
|
185
|
+
showMessage('Try again!', 'info');
|
|
186
186
|
}
|
|
187
187
|
|
|
188
188
|
updateDisplay();
|
|
@@ -273,10 +273,10 @@ function gameOver() {
|
|
|
273
273
|
ctx.fillStyle = '#FFFFFF';
|
|
274
274
|
ctx.font = 'bold 36px Arial';
|
|
275
275
|
ctx.textAlign = 'center';
|
|
276
|
-
ctx.fillText('
|
|
276
|
+
ctx.fillText('Game Over!', canvas.width / 2, canvas.height / 2 - 30);
|
|
277
277
|
|
|
278
278
|
ctx.font = '20px Arial';
|
|
279
|
-
ctx.fillText(
|
|
279
|
+
ctx.fillText(`Score: ${gameState.score}`, canvas.width / 2, canvas.height / 2 + 10);
|
|
280
280
|
|
|
281
281
|
document.getElementById('startBtn').disabled = false;
|
|
282
282
|
document.getElementById('pauseBtn').disabled = true;
|
|
@@ -324,7 +324,7 @@ function draw() {
|
|
|
324
324
|
ctx.fillStyle = '#FFFFFF';
|
|
325
325
|
ctx.font = 'bold 28px Arial';
|
|
326
326
|
ctx.textAlign = 'center';
|
|
327
|
-
ctx.fillText('
|
|
327
|
+
ctx.fillText('Game Paused', canvas.width / 2, canvas.height / 2);
|
|
328
328
|
}
|
|
329
329
|
}
|
|
330
330
|
|
|
@@ -408,7 +408,7 @@ function startGame() {
|
|
|
408
408
|
// Pause game
|
|
409
409
|
function pauseGame() {
|
|
410
410
|
gameState.paused = !gameState.paused;
|
|
411
|
-
document.getElementById('pauseBtn').textContent = gameState.paused ? '
|
|
411
|
+
document.getElementById('pauseBtn').textContent = gameState.paused ? 'Resume' : 'Pause';
|
|
412
412
|
}
|
|
413
413
|
|
|
414
414
|
// Reset game
|
|
@@ -432,7 +432,7 @@ function resetGame() {
|
|
|
432
432
|
|
|
433
433
|
document.getElementById('startBtn').disabled = false;
|
|
434
434
|
document.getElementById('pauseBtn').disabled = true;
|
|
435
|
-
document.getElementById('pauseBtn').textContent = '
|
|
435
|
+
document.getElementById('pauseBtn').textContent = 'Pause';
|
|
436
436
|
}
|
|
437
437
|
|
|
438
438
|
// Event listeners
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# React Components Templates 🎨⚛️
|
|
2
|
+
|
|
3
|
+
This directory contains React component templates that can be generated using the `create-template` CLI tool with React support.
|
|
4
|
+
|
|
5
|
+
## Available Components
|
|
6
|
+
|
|
7
|
+
### 🔘 Button
|
|
8
|
+
A customizable button component with various styles and states:
|
|
9
|
+
- **Variants**: primary, secondary, success, danger
|
|
10
|
+
- **Sizes**: small, medium, large
|
|
11
|
+
- **States**: enabled, disabled
|
|
12
|
+
- **Props**: variant, size, disabled, onClick, type, className
|
|
13
|
+
|
|
14
|
+
### 🎴 Card
|
|
15
|
+
A versatile card component for displaying content:
|
|
16
|
+
- **Features**: image support, title, description, footer
|
|
17
|
+
- **Responsive design**
|
|
18
|
+
- **Hover effects**
|
|
19
|
+
- **Dark mode support**
|
|
20
|
+
- **Props**: title, description, image, imageAlt, footer, onClick, className
|
|
21
|
+
|
|
22
|
+
### 🔢 Counter
|
|
23
|
+
A simple counter with increment and decrement functionality:
|
|
24
|
+
- **Features**: customizable initial value, min/max limits, custom step
|
|
25
|
+
- **Animations**: smooth transitions
|
|
26
|
+
- **Controls**: increment, decrement, reset buttons
|
|
27
|
+
- **Props**: initialValue, min, max, step, onChange
|
|
28
|
+
|
|
29
|
+
### 📝 Form
|
|
30
|
+
A flexible form component with validation:
|
|
31
|
+
- **Features**: multiple field types (text, email, textarea, select)
|
|
32
|
+
- **Validation**: required fields, patterns, min length
|
|
33
|
+
- **Error messages**
|
|
34
|
+
- **Dark mode support**
|
|
35
|
+
- **Props**: title, fields, onSubmit, submitButtonText
|
|
36
|
+
|
|
37
|
+
### 🪟 Modal
|
|
38
|
+
A flexible modal dialog component:
|
|
39
|
+
- **Features**: overlay click to close, customizable size
|
|
40
|
+
- **Sizes**: small, medium, large
|
|
41
|
+
- **Animations**: fade in, slide up
|
|
42
|
+
- **Dark mode support**
|
|
43
|
+
- **Props**: isOpen, onClose, title, children, footer, showCloseButton, closeOnOverlayClick, size
|
|
44
|
+
|
|
45
|
+
### ✅ Todo List
|
|
46
|
+
A complete todo list with add, toggle, and delete functionality:
|
|
47
|
+
- **Features**: add tasks, mark as complete, delete tasks
|
|
48
|
+
- **Statistics**: active and completed count
|
|
49
|
+
- **Animations**: smooth transitions
|
|
50
|
+
- **Dark mode support**
|
|
51
|
+
- **Local state management**
|
|
52
|
+
|
|
53
|
+
## Usage
|
|
54
|
+
|
|
55
|
+
### Creating a React Component
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# Interactive mode
|
|
59
|
+
npx create-template-html-css create --react
|
|
60
|
+
|
|
61
|
+
# With flags
|
|
62
|
+
npx create-template-html-css create --react --component button --name my-button
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Importing in Your Project
|
|
66
|
+
|
|
67
|
+
```jsx
|
|
68
|
+
import Button from './components/Button/Button';
|
|
69
|
+
import './components/Button/Button.css';
|
|
70
|
+
|
|
71
|
+
function App() {
|
|
72
|
+
return (
|
|
73
|
+
<Button variant="primary" onClick={() => console.log('Clicked!')}>
|
|
74
|
+
Click Me
|
|
75
|
+
</Button>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Component Structure
|
|
81
|
+
|
|
82
|
+
Each component follows this structure:
|
|
83
|
+
```
|
|
84
|
+
component-name/
|
|
85
|
+
├── ComponentName.jsx # Main component
|
|
86
|
+
├── ComponentName.css # Styles
|
|
87
|
+
└── ComponentName.example.jsx # Usage examples
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Styling
|
|
91
|
+
|
|
92
|
+
All components support:
|
|
93
|
+
- ✨ Gradient backgrounds with customizable colors
|
|
94
|
+
- 🌙 Dark mode (prefers-color-scheme)
|
|
95
|
+
- 📱 Responsive design
|
|
96
|
+
- 🎭 Smooth animations and transitions
|
|
97
|
+
- 🎨 CSS color placeholders ({{primaryColor}}, {{secondaryColor}})
|
|
98
|
+
|
|
99
|
+
## Color Customization
|
|
100
|
+
|
|
101
|
+
Colors can be customized during generation:
|
|
102
|
+
- `--primary-color`: Main brand color
|
|
103
|
+
- `--secondary-color`: Accent color
|
|
104
|
+
- `--color-scheme`: Predefined schemes (ocean, sunset, forest, etc.)
|
|
105
|
+
|
|
106
|
+
## Requirements
|
|
107
|
+
|
|
108
|
+
These components are designed for use with:
|
|
109
|
+
- React 16.8+ (Hooks support)
|
|
110
|
+
- Modern browsers (ES6+)
|
|
111
|
+
- CSS3 support
|
|
112
|
+
|
|
113
|
+
## Contributing
|
|
114
|
+
|
|
115
|
+
To add a new React component template:
|
|
116
|
+
1. Create a new directory in `templates-react/`
|
|
117
|
+
2. Add `ComponentName.jsx`, `ComponentName.css`, and `ComponentName.example.jsx`
|
|
118
|
+
3. Follow the existing component structure
|
|
119
|
+
4. Update this README
|
|
120
|
+
|
|
121
|
+
## Notes
|
|
122
|
+
|
|
123
|
+
- All components use functional components with hooks
|
|
124
|
+
- PropTypes or TypeScript types can be added as needed
|
|
125
|
+
- Components are fully self-contained with their own styles
|
|
126
|
+
- CSS uses placeholders for color customization during generation
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/* Button Styles */
|
|
2
|
+
.btn {
|
|
3
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
|
4
|
+
font-weight: 600;
|
|
5
|
+
border: none;
|
|
6
|
+
border-radius: 8px;
|
|
7
|
+
cursor: pointer;
|
|
8
|
+
transition: all 0.3s ease;
|
|
9
|
+
outline: none;
|
|
10
|
+
display: inline-flex;
|
|
11
|
+
align-items: center;
|
|
12
|
+
justify-content: center;
|
|
13
|
+
gap: 8px;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/* Size Variations */
|
|
17
|
+
.btn-small {
|
|
18
|
+
padding: 8px 16px;
|
|
19
|
+
font-size: 14px;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.btn-medium {
|
|
23
|
+
padding: 12px 24px;
|
|
24
|
+
font-size: 16px;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.btn-large {
|
|
28
|
+
padding: 16px 32px;
|
|
29
|
+
font-size: 18px;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* Variant Styles - Primary */
|
|
33
|
+
.btn-primary {
|
|
34
|
+
background: linear-gradient(135deg, {{primaryColor}} 0%, {{secondaryColor}} 100%);
|
|
35
|
+
color: white;
|
|
36
|
+
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.btn-primary:hover:not(:disabled) {
|
|
40
|
+
transform: translateY(-2px);
|
|
41
|
+
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.btn-primary:active:not(:disabled) {
|
|
45
|
+
transform: translateY(0);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* Variant Styles - Secondary */
|
|
49
|
+
.btn-secondary {
|
|
50
|
+
background: transparent;
|
|
51
|
+
color: {{primaryColor}};
|
|
52
|
+
border: 2px solid {{primaryColor}};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.btn-secondary:hover:not(:disabled) {
|
|
56
|
+
background: {{primaryColor}};
|
|
57
|
+
color: white;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* Variant Styles - Danger */
|
|
61
|
+
.btn-danger {
|
|
62
|
+
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 100%);
|
|
63
|
+
color: white;
|
|
64
|
+
box-shadow: 0 4px 15px rgba(255, 107, 107, 0.3);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.btn-danger:hover:not(:disabled) {
|
|
68
|
+
transform: translateY(-2px);
|
|
69
|
+
box-shadow: 0 6px 20px rgba(255, 107, 107, 0.4);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* Variant Styles - Success */
|
|
73
|
+
.btn-success {
|
|
74
|
+
background: linear-gradient(135deg, #51cf66 0%, #37b24d 100%);
|
|
75
|
+
color: white;
|
|
76
|
+
box-shadow: 0 4px 15px rgba(81, 207, 102, 0.3);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.btn-success:hover:not(:disabled) {
|
|
80
|
+
transform: translateY(-2px);
|
|
81
|
+
box-shadow: 0 6px 20px rgba(81, 207, 102, 0.4);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* Disabled State */
|
|
85
|
+
.btn:disabled {
|
|
86
|
+
opacity: 0.5;
|
|
87
|
+
cursor: not-allowed;
|
|
88
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Button from './Button';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Example usage of Button component
|
|
6
|
+
*/
|
|
7
|
+
const ButtonExample = () => {
|
|
8
|
+
const handleClick = () => {
|
|
9
|
+
console.log('Button clicked!');
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<div style={{ padding: '40px', display: 'flex', flexDirection: 'column', gap: '20px' }}>
|
|
14
|
+
<h2>Button Component Examples</h2>
|
|
15
|
+
|
|
16
|
+
<div style={{ display: 'flex', gap: '10px', flexWrap: 'wrap' }}>
|
|
17
|
+
<h3 style={{ width: '100%' }}>Variants</h3>
|
|
18
|
+
<Button variant="primary" onClick={handleClick}>Primary Button</Button>
|
|
19
|
+
<Button variant="secondary" onClick={handleClick}>Secondary Button</Button>
|
|
20
|
+
<Button variant="success" onClick={handleClick}>Success Button</Button>
|
|
21
|
+
<Button variant="danger" onClick={handleClick}>Danger Button</Button>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<div style={{ display: 'flex', gap: '10px', flexWrap: 'wrap' }}>
|
|
25
|
+
<h3 style={{ width: '100%' }}>Sizes</h3>
|
|
26
|
+
<Button size="small" onClick={handleClick}>Small</Button>
|
|
27
|
+
<Button size="medium" onClick={handleClick}>Medium</Button>
|
|
28
|
+
<Button size="large" onClick={handleClick}>Large</Button>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<div style={{ display: 'flex', gap: '10px', flexWrap: 'wrap' }}>
|
|
32
|
+
<h3 style={{ width: '100%' }}>States</h3>
|
|
33
|
+
<Button onClick={handleClick}>Enabled</Button>
|
|
34
|
+
<Button disabled>Disabled</Button>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export default ButtonExample;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import './Button.css';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Button Component
|
|
6
|
+
* A customizable button component with various styles and states
|
|
7
|
+
*/
|
|
8
|
+
const Button = ({
|
|
9
|
+
children,
|
|
10
|
+
variant = 'primary',
|
|
11
|
+
size = 'medium',
|
|
12
|
+
disabled = false,
|
|
13
|
+
onClick,
|
|
14
|
+
type = 'button',
|
|
15
|
+
className = ''
|
|
16
|
+
}) => {
|
|
17
|
+
return (
|
|
18
|
+
<button
|
|
19
|
+
type={type}
|
|
20
|
+
className={`btn btn-${variant} btn-${size} ${className}`}
|
|
21
|
+
disabled={disabled}
|
|
22
|
+
onClick={onClick}
|
|
23
|
+
>
|
|
24
|
+
{children}
|
|
25
|
+
</button>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export default Button;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/* Card Styles */
|
|
2
|
+
.card {
|
|
3
|
+
background: white;
|
|
4
|
+
border-radius: 12px;
|
|
5
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
|
6
|
+
overflow: hidden;
|
|
7
|
+
transition: all 0.3s ease;
|
|
8
|
+
cursor: pointer;
|
|
9
|
+
max-width: 400px;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.card:hover {
|
|
13
|
+
transform: translateY(-5px);
|
|
14
|
+
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.15);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.card-image {
|
|
18
|
+
width: 100%;
|
|
19
|
+
height: 200px;
|
|
20
|
+
overflow: hidden;
|
|
21
|
+
position: relative;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.card-image img {
|
|
25
|
+
width: 100%;
|
|
26
|
+
height: 100%;
|
|
27
|
+
object-fit: cover;
|
|
28
|
+
transition: transform 0.3s ease;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.card:hover .card-image img {
|
|
32
|
+
transform: scale(1.05);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.card-content {
|
|
36
|
+
padding: 24px;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.card-title {
|
|
40
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
|
41
|
+
font-size: 24px;
|
|
42
|
+
font-weight: 700;
|
|
43
|
+
margin: 0 0 12px 0;
|
|
44
|
+
color: #2d3748;
|
|
45
|
+
background: linear-gradient(135deg, {{primaryColor}} 0%, {{secondaryColor}} 100%);
|
|
46
|
+
-webkit-background-clip: text;
|
|
47
|
+
-webkit-text-fill-color: transparent;
|
|
48
|
+
background-clip: text;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.card-description {
|
|
52
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
|
53
|
+
font-size: 16px;
|
|
54
|
+
line-height: 1.6;
|
|
55
|
+
color: #4a5568;
|
|
56
|
+
margin: 0;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.card-footer {
|
|
60
|
+
padding: 16px 24px;
|
|
61
|
+
border-top: 1px solid #e2e8f0;
|
|
62
|
+
background: #f7fafc;
|
|
63
|
+
display: flex;
|
|
64
|
+
justify-content: space-between;
|
|
65
|
+
align-items: center;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/* Dark Mode Support */
|
|
69
|
+
@media (prefers-color-scheme: dark) {
|
|
70
|
+
.card {
|
|
71
|
+
background: #2d3748;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.card-title {
|
|
75
|
+
color: #e2e8f0;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.card-description {
|
|
79
|
+
color: #cbd5e0;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.card-footer {
|
|
83
|
+
background: #1a202c;
|
|
84
|
+
border-top-color: #4a5568;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Card from './Card';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Example usage of Card component
|
|
6
|
+
*/
|
|
7
|
+
const CardExample = () => {
|
|
8
|
+
const handleCardClick = () => {
|
|
9
|
+
console.log('Card clicked!');
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<div style={{ padding: '40px', display: 'flex', gap: '20px', flexWrap: 'wrap' }}>
|
|
14
|
+
<Card
|
|
15
|
+
title="Beautiful Mountain"
|
|
16
|
+
description="Explore the breathtaking views of mountain landscapes and discover the beauty of nature."
|
|
17
|
+
image="https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=400&h=200&fit=crop"
|
|
18
|
+
imageAlt="Mountain landscape"
|
|
19
|
+
footer={
|
|
20
|
+
<button style={{ padding: '8px 16px', background: '#3182ce', color: 'white', border: 'none', borderRadius: '6px' }}>
|
|
21
|
+
Learn More
|
|
22
|
+
</button>
|
|
23
|
+
}
|
|
24
|
+
onClick={handleCardClick}
|
|
25
|
+
/>
|
|
26
|
+
|
|
27
|
+
<Card
|
|
28
|
+
title="Ocean Sunset"
|
|
29
|
+
description="Watch the sun set over the endless horizon and feel the peaceful ocean breeze."
|
|
30
|
+
image="https://images.unsplash.com/photo-1505142468610-359e7d316be0?w=400&h=200&fit=crop"
|
|
31
|
+
imageAlt="Ocean sunset"
|
|
32
|
+
footer={
|
|
33
|
+
<button style={{ padding: '8px 16px', background: '#3182ce', color: 'white', border: 'none', borderRadius: '6px' }}>
|
|
34
|
+
Learn More
|
|
35
|
+
</button>
|
|
36
|
+
}
|
|
37
|
+
onClick={handleCardClick}
|
|
38
|
+
/>
|
|
39
|
+
|
|
40
|
+
<Card
|
|
41
|
+
title="Card Without Image"
|
|
42
|
+
description="This card demonstrates how the component looks without an image. It still maintains the same elegant design."
|
|
43
|
+
footer={<span style={{ color: '#718096' }}>No image variant</span>}
|
|
44
|
+
/>
|
|
45
|
+
</div>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export default CardExample;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import './Card.css';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Card Component
|
|
6
|
+
* A versatile card component for displaying content
|
|
7
|
+
*/
|
|
8
|
+
const Card = ({
|
|
9
|
+
title,
|
|
10
|
+
description,
|
|
11
|
+
image,
|
|
12
|
+
imageAlt = '',
|
|
13
|
+
footer,
|
|
14
|
+
onClick,
|
|
15
|
+
className = '',
|
|
16
|
+
children
|
|
17
|
+
}) => {
|
|
18
|
+
return (
|
|
19
|
+
<div className={`card ${className}`} onClick={onClick}>
|
|
20
|
+
{image && (
|
|
21
|
+
<div className="card-image">
|
|
22
|
+
<img src={image} alt={imageAlt} />
|
|
23
|
+
</div>
|
|
24
|
+
)}
|
|
25
|
+
<div className="card-content">
|
|
26
|
+
{title && <h3 className="card-title">{title}</h3>}
|
|
27
|
+
{description && <p className="card-description">{description}</p>}
|
|
28
|
+
{children}
|
|
29
|
+
</div>
|
|
30
|
+
{footer && <div className="card-footer">{footer}</div>}
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export default Card;
|