create-template-html-css 2.0.4 → 2.2.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.
Files changed (99) hide show
  1. package/CHANGELOG.md +436 -0
  2. package/CODE-SPLITTING-GUIDE.md +274 -0
  3. package/COMPONENTS-GALLERY.html +143 -8
  4. package/HTML-VS-REACT.md +289 -0
  5. package/QUICKSTART-REACT.md +293 -0
  6. package/REACT-SUPPORT-SUMMARY.md +235 -0
  7. package/README.md +261 -12
  8. package/bin/cli.js +100 -759
  9. package/bin/commands/create.js +288 -0
  10. package/bin/commands/gallery.js +42 -0
  11. package/bin/commands/insert.js +123 -0
  12. package/bin/commands/list.js +73 -0
  13. package/package.json +10 -3
  14. package/src/component-choices.js +7 -0
  15. package/src/components-registry.js +112 -0
  16. package/src/format-utils.js +49 -0
  17. package/src/generator.js +83 -594
  18. package/src/generators/color-schemes.js +78 -0
  19. package/src/generators/color-utils.js +108 -0
  20. package/src/generators/component-filters.js +151 -0
  21. package/src/generators/html-generators.js +180 -0
  22. package/src/generators/validation.js +43 -0
  23. package/src/index.js +2 -1
  24. package/src/inserter.js +55 -233
  25. package/src/inserters/backup-utils.js +20 -0
  26. package/src/inserters/component-loader.js +68 -0
  27. package/src/inserters/html-utils.js +31 -0
  28. package/src/inserters/indentation-utils.js +90 -0
  29. package/src/inserters/validation-utils.js +49 -0
  30. package/src/react-component-choices.js +97 -0
  31. package/src/react-component-templates.js +182 -0
  32. package/src/react-file-operations.js +172 -0
  33. package/src/react-generator.js +219 -0
  34. package/src/react-templates.js +418 -0
  35. package/src/templates/basic-components-templates.js +157 -0
  36. package/src/templates/form-components-templates.js +194 -0
  37. package/src/templates/interactive-components-templates.js +139 -0
  38. package/src/utils/file-utils.js +97 -0
  39. package/src/utils/path-utils.js +32 -0
  40. package/src/utils/string-utils.js +51 -0
  41. package/src/utils/template-loader.js +91 -0
  42. package/templates/_shared/PATTERNS.md +246 -0
  43. package/templates/_shared/README.md +74 -0
  44. package/templates/_shared/base.css +18 -0
  45. package/templates/blackjack/index.html +1 -1
  46. package/templates/breakout/index.html +1 -1
  47. package/templates/connect-four/index.html +1 -1
  48. package/templates/dice-game/index.html +1 -1
  49. package/templates/flappy-bird/index.html +1 -1
  50. package/templates/pong/index.html +1 -1
  51. package/templates/skeleton/index.html +4 -4
  52. package/templates/slot-machine/index.html +1 -1
  53. package/templates/tetris/index.html +1 -1
  54. package/templates-react/README.md +126 -0
  55. package/templates-react/alert/Alert.css +158 -0
  56. package/templates-react/alert/Alert.example.jsx +106 -0
  57. package/templates-react/alert/Alert.jsx +61 -0
  58. package/templates-react/badge/Badge.css +196 -0
  59. package/templates-react/badge/Badge.example.jsx +182 -0
  60. package/templates-react/badge/Badge.jsx +44 -0
  61. package/templates-react/button/Button.css +88 -0
  62. package/templates-react/button/Button.example.jsx +40 -0
  63. package/templates-react/button/Button.jsx +29 -0
  64. package/templates-react/card/Card.css +86 -0
  65. package/templates-react/card/Card.example.jsx +49 -0
  66. package/templates-react/card/Card.jsx +35 -0
  67. package/templates-react/checkbox/Checkbox.css +217 -0
  68. package/templates-react/checkbox/Checkbox.example.jsx +141 -0
  69. package/templates-react/checkbox/Checkbox.jsx +82 -0
  70. package/templates-react/counter/Counter.css +99 -0
  71. package/templates-react/counter/Counter.example.jsx +45 -0
  72. package/templates-react/counter/Counter.jsx +70 -0
  73. package/templates-react/dropdown/Dropdown.css +237 -0
  74. package/templates-react/dropdown/Dropdown.example.jsx +98 -0
  75. package/templates-react/dropdown/Dropdown.jsx +154 -0
  76. package/templates-react/form/Form.css +128 -0
  77. package/templates-react/form/Form.example.jsx +64 -0
  78. package/templates-react/form/Form.jsx +125 -0
  79. package/templates-react/input/Input.css +113 -0
  80. package/templates-react/input/Input.example.jsx +82 -0
  81. package/templates-react/input/Input.jsx +87 -0
  82. package/templates-react/modal/Modal.css +152 -0
  83. package/templates-react/modal/Modal.example.jsx +90 -0
  84. package/templates-react/modal/Modal.jsx +46 -0
  85. package/templates-react/navbar/Navbar.css +139 -0
  86. package/templates-react/navbar/Navbar.example.jsx +37 -0
  87. package/templates-react/navbar/Navbar.jsx +62 -0
  88. package/templates-react/progress/Progress.css +247 -0
  89. package/templates-react/progress/Progress.example.jsx +244 -0
  90. package/templates-react/progress/Progress.jsx +79 -0
  91. package/templates-react/switch/Switch.css +244 -0
  92. package/templates-react/switch/Switch.example.jsx +221 -0
  93. package/templates-react/switch/Switch.jsx +98 -0
  94. package/templates-react/todo-list/TodoList.css +236 -0
  95. package/templates-react/todo-list/TodoList.example.jsx +15 -0
  96. package/templates-react/todo-list/TodoList.jsx +84 -0
  97. package/templates-react/tooltip/Tooltip.css +165 -0
  98. package/templates-react/tooltip/Tooltip.example.jsx +166 -0
  99. package/templates-react/tooltip/Tooltip.jsx +176 -0
@@ -0,0 +1,98 @@
1
+ import { useState } from 'react';
2
+ import Dropdown from './Dropdown';
3
+
4
+ function DropdownExample() {
5
+ const [selected, setSelected] = useState('');
6
+ const [searchableSelected, setSearchableSelected] = useState('');
7
+ const [multiSelected, setMultiSelected] = useState([]);
8
+
9
+ const simpleOptions = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry'];
10
+
11
+ const complexOptions = [
12
+ { value: 'react', label: 'React' },
13
+ { value: 'vue', label: 'Vue.js' },
14
+ { value: 'angular', label: 'Angular' },
15
+ { value: 'svelte', label: 'Svelte' },
16
+ { value: 'next', label: 'Next.js' }
17
+ ];
18
+
19
+ const countries = [
20
+ { value: 'us', label: 'United States' },
21
+ { value: 'uk', label: 'United Kingdom' },
22
+ { value: 'ca', label: 'Canada' },
23
+ { value: 'au', label: 'Australia' },
24
+ { value: 'de', label: 'Germany' },
25
+ { value: 'fr', label: 'France' },
26
+ { value: 'jp', label: 'Japan' },
27
+ { value: 'cn', label: 'China' },
28
+ { value: 'in', label: 'India' },
29
+ { value: 'br', label: 'Brazil' }
30
+ ];
31
+
32
+ return (
33
+ <div style={{ padding: '2rem', maxWidth: '600px', margin: '0 auto' }}>
34
+ <h1>Dropdown Component Examples</h1>
35
+
36
+ <section style={{ marginBottom: '2rem' }}>
37
+ <h2>Basic Dropdown</h2>
38
+ <Dropdown
39
+ label="Choose a fruit"
40
+ options={simpleOptions}
41
+ value={selected}
42
+ onChange={setSelected}
43
+ placeholder="Select a fruit"
44
+ />
45
+ <p>Selected: {selected || 'None'}</p>
46
+ </section>
47
+
48
+ <section style={{ marginBottom: '2rem' }}>
49
+ <h2>Searchable Dropdown</h2>
50
+ <Dropdown
51
+ label="Select your country"
52
+ options={countries}
53
+ value={searchableSelected}
54
+ onChange={setSearchableSelected}
55
+ placeholder="Search and select..."
56
+ searchable
57
+ />
58
+ <p>Selected: {searchableSelected || 'None'}</p>
59
+ </section>
60
+
61
+ <section style={{ marginBottom: '2rem' }}>
62
+ <h2>Multiple Selection</h2>
63
+ <Dropdown
64
+ label="Choose frameworks"
65
+ options={complexOptions}
66
+ value={multiSelected}
67
+ onChange={setMultiSelected}
68
+ placeholder="Select frameworks"
69
+ multiple
70
+ searchable
71
+ />
72
+ <p>Selected: {multiSelected.join(', ') || 'None'}</p>
73
+ </section>
74
+
75
+ <section style={{ marginBottom: '2rem' }}>
76
+ <h2>Disabled Dropdown</h2>
77
+ <Dropdown
78
+ label="Disabled dropdown"
79
+ options={simpleOptions}
80
+ placeholder="Cannot select"
81
+ disabled
82
+ />
83
+ </section>
84
+
85
+ <section style={{ marginBottom: '2rem' }}>
86
+ <h2>With Error</h2>
87
+ <Dropdown
88
+ label="Required field"
89
+ options={simpleOptions}
90
+ placeholder="Please select"
91
+ error="This field is required"
92
+ />
93
+ </section>
94
+ </div>
95
+ );
96
+ }
97
+
98
+ export default DropdownExample;
@@ -0,0 +1,154 @@
1
+
2
+ import './Dropdown.css';
3
+ import { useState, useRef, useEffect } from 'react';
4
+
5
+ function Dropdown({
6
+ options = [],
7
+ value,
8
+ onChange,
9
+ placeholder = 'Select an option',
10
+ disabled = false,
11
+ searchable = false,
12
+ label = '',
13
+ error = '',
14
+ multiple = false,
15
+ maxHeight = '200px'
16
+ }) {
17
+ const [isOpen, setIsOpen] = useState(false);
18
+ const [searchTerm, setSearchTerm] = useState('');
19
+ const [selectedValues, setSelectedValues] = useState(multiple ? (value || []) : null);
20
+ const dropdownRef = useRef(null);
21
+
22
+ useEffect(() => {
23
+ const handleClickOutside = (event) => {
24
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
25
+ setIsOpen(false);
26
+ setSearchTerm('');
27
+ }
28
+ };
29
+
30
+ document.addEventListener('mousedown', handleClickOutside);
31
+ return () => document.removeEventListener('mousedown', handleClickOutside);
32
+ }, []);
33
+
34
+ const filteredOptions = searchable && searchTerm
35
+ ? options.filter(option =>
36
+ typeof option === 'string'
37
+ ? option.toLowerCase().includes(searchTerm.toLowerCase())
38
+ : option.label.toLowerCase().includes(searchTerm.toLowerCase())
39
+ )
40
+ : options;
41
+
42
+ const handleSelect = (option) => {
43
+ const optionValue = typeof option === 'string' ? option : option.value;
44
+
45
+ if (multiple) {
46
+ const newValues = selectedValues.includes(optionValue)
47
+ ? selectedValues.filter(v => v !== optionValue)
48
+ : [...selectedValues, optionValue];
49
+ setSelectedValues(newValues);
50
+ onChange?.(newValues);
51
+ } else {
52
+ setSelectedValues(optionValue);
53
+ onChange?.(optionValue);
54
+ setIsOpen(false);
55
+ setSearchTerm('');
56
+ }
57
+ };
58
+
59
+ const getDisplayValue = () => {
60
+ if (multiple && selectedValues.length > 0) {
61
+ return `${selectedValues.length} selected`;
62
+ }
63
+ if (!multiple && selectedValues) {
64
+ const selected = options.find(opt =>
65
+ (typeof opt === 'string' ? opt : opt.value) === selectedValues
66
+ );
67
+ return typeof selected === 'string' ? selected : selected?.label || selectedValues;
68
+ }
69
+ return placeholder;
70
+ };
71
+
72
+ const isSelected = (option) => {
73
+ const optionValue = typeof option === 'string' ? option : option.value;
74
+ return multiple
75
+ ? selectedValues.includes(optionValue)
76
+ : selectedValues === optionValue;
77
+ };
78
+
79
+ return (
80
+ <div className={`dropdown-container ${error ? 'dropdown-error' : ''}`}>
81
+ {label && <label className="dropdown-label">{label}</label>}
82
+
83
+ <div
84
+ ref={dropdownRef}
85
+ className={`dropdown ${isOpen ? 'open' : ''} ${disabled ? 'disabled' : ''}`}
86
+ >
87
+ <button
88
+ type="button"
89
+ className="dropdown-toggle"
90
+ onClick={() => !disabled && setIsOpen(!isOpen)}
91
+ disabled={disabled}
92
+ aria-haspopup="listbox"
93
+ aria-expanded={isOpen}
94
+ >
95
+ <span className="dropdown-value">{getDisplayValue()}</span>
96
+ <span className={`dropdown-arrow ${isOpen ? 'up' : 'down'}`}>▼</span>
97
+ </button>
98
+
99
+ {isOpen && (
100
+ <div className="dropdown-menu" style={{ maxHeight }}>
101
+ {searchable && (
102
+ <div className="dropdown-search">
103
+ <input
104
+ type="text"
105
+ placeholder="Search..."
106
+ value={searchTerm}
107
+ onChange={(e) => setSearchTerm(e.target.value)}
108
+ onClick={(e) => e.stopPropagation()}
109
+ className="dropdown-search-input"
110
+ />
111
+ </div>
112
+ )}
113
+
114
+ <ul className="dropdown-options" role="listbox">
115
+ {filteredOptions.length === 0 ? (
116
+ <li className="dropdown-option disabled">No options available</li>
117
+ ) : (
118
+ filteredOptions.map((option, index) => {
119
+ const optionValue = typeof option === 'string' ? option : option.value;
120
+ const optionLabel = typeof option === 'string' ? option : option.label;
121
+ const selected = isSelected(option);
122
+
123
+ return (
124
+ <li
125
+ key={optionValue || index}
126
+ className={`dropdown-option ${selected ? 'selected' : ''}`}
127
+ onClick={() => handleSelect(option)}
128
+ role="option"
129
+ aria-selected={selected}
130
+ >
131
+ {multiple && (
132
+ <input
133
+ type="checkbox"
134
+ checked={selected}
135
+ onChange={() => {}}
136
+ className="dropdown-checkbox"
137
+ />
138
+ )}
139
+ {optionLabel}
140
+ </li>
141
+ );
142
+ })
143
+ )}
144
+ </ul>
145
+ </div>
146
+ )}
147
+ </div>
148
+
149
+ {error && <span className="dropdown-error-message">{error}</span>}
150
+ </div>
151
+ );
152
+ }
153
+
154
+ export default Dropdown;
@@ -0,0 +1,128 @@
1
+ /* Form Styles */
2
+ .form-container {
3
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
4
+ background: white;
5
+ padding: 40px;
6
+ border-radius: 20px;
7
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
8
+ max-width: 500px;
9
+ margin: 0 auto;
10
+ }
11
+
12
+ .form-title {
13
+ font-size: 32px;
14
+ font-weight: 700;
15
+ margin: 0 0 30px 0;
16
+ text-align: center;
17
+ background: linear-gradient(135deg, {{primaryColor}} 0%, {{secondaryColor}} 100%);
18
+ -webkit-background-clip: text;
19
+ -webkit-text-fill-color: transparent;
20
+ background-clip: text;
21
+ }
22
+
23
+ .form {
24
+ display: flex;
25
+ flex-direction: column;
26
+ gap: 20px;
27
+ }
28
+
29
+ .form-group {
30
+ display: flex;
31
+ flex-direction: column;
32
+ gap: 8px;
33
+ }
34
+
35
+ .form-label {
36
+ font-size: 14px;
37
+ font-weight: 600;
38
+ color: #2d3748;
39
+ display: flex;
40
+ align-items: center;
41
+ gap: 4px;
42
+ }
43
+
44
+ .required {
45
+ color: #e53e3e;
46
+ }
47
+
48
+ .form input,
49
+ .form textarea,
50
+ .form select {
51
+ padding: 12px 16px;
52
+ font-size: 16px;
53
+ font-family: inherit;
54
+ border: 2px solid #e2e8f0;
55
+ border-radius: 8px;
56
+ transition: all 0.3s ease;
57
+ outline: none;
58
+ background: white;
59
+ }
60
+
61
+ .form input:focus,
62
+ .form textarea:focus,
63
+ .form select:focus {
64
+ border-color: {{primaryColor}};
65
+ box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.1);
66
+ }
67
+
68
+ .form textarea {
69
+ resize: vertical;
70
+ min-height: 100px;
71
+ }
72
+
73
+ .form-error {
74
+ font-size: 14px;
75
+ color: #e53e3e;
76
+ display: flex;
77
+ align-items: center;
78
+ gap: 4px;
79
+ }
80
+
81
+ .form-submit {
82
+ margin-top: 10px;
83
+ padding: 16px 32px;
84
+ font-size: 16px;
85
+ font-weight: 600;
86
+ font-family: inherit;
87
+ background: linear-gradient(135deg, {{primaryColor}} 0%, {{secondaryColor}} 100%);
88
+ color: white;
89
+ border: none;
90
+ border-radius: 8px;
91
+ cursor: pointer;
92
+ transition: all 0.3s ease;
93
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
94
+ }
95
+
96
+ .form-submit:hover {
97
+ transform: translateY(-2px);
98
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
99
+ }
100
+
101
+ .form-submit:active {
102
+ transform: translateY(0);
103
+ }
104
+
105
+ /* Dark Mode Support */
106
+ @media (prefers-color-scheme: dark) {
107
+ .form-container {
108
+ background: #2d3748;
109
+ }
110
+
111
+ .form-label {
112
+ color: #e2e8f0;
113
+ }
114
+
115
+ .form input,
116
+ .form textarea,
117
+ .form select {
118
+ background: #1a202c;
119
+ color: #e2e8f0;
120
+ border-color: #4a5568;
121
+ }
122
+
123
+ .form input:focus,
124
+ .form textarea:focus,
125
+ .form select:focus {
126
+ border-color: {{primaryColor}};
127
+ }
128
+ }
@@ -0,0 +1,64 @@
1
+ import Form from './Form';
2
+
3
+ /**
4
+ * Example usage of Form component
5
+ */
6
+ const FormExample = () => {
7
+ const contactFields = [
8
+ {
9
+ name: 'name',
10
+ label: 'Full Name',
11
+ type: 'text',
12
+ required: true,
13
+ placeholder: 'John Doe',
14
+ minLength: 2
15
+ },
16
+ {
17
+ name: 'email',
18
+ label: 'Email Address',
19
+ type: 'email',
20
+ required: true,
21
+ placeholder: 'john@example.com',
22
+ pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
23
+ errorMessage: 'Please enter a valid email address'
24
+ },
25
+ {
26
+ name: 'subject',
27
+ label: 'Subject',
28
+ type: 'select',
29
+ required: true,
30
+ options: [
31
+ { value: 'general', label: 'General Inquiry' },
32
+ { value: 'support', label: 'Technical Support' },
33
+ { value: 'feedback', label: 'Feedback' }
34
+ ]
35
+ },
36
+ {
37
+ name: 'message',
38
+ label: 'Message',
39
+ type: 'textarea',
40
+ required: true,
41
+ placeholder: 'Type your message here...',
42
+ rows: 6,
43
+ minLength: 10
44
+ }
45
+ ];
46
+
47
+ const handleSubmit = (data) => {
48
+ console.log('Form submitted with data:', data);
49
+ alert('Form submitted successfully! Check console for details.');
50
+ };
51
+
52
+ return (
53
+ <div style={{ padding: '40px', maxWidth: '600px', margin: '0 auto' }}>
54
+ <Form
55
+ title="Contact Us"
56
+ fields={contactFields}
57
+ onSubmit={handleSubmit}
58
+ submitButtonText="Send Message"
59
+ />
60
+ </div>
61
+ );
62
+ };
63
+
64
+ export default FormExample;
@@ -0,0 +1,125 @@
1
+ import { useState } from 'react';
2
+ import './Form.css';
3
+
4
+ /**
5
+ * Form Component
6
+ * A flexible form component with validation
7
+ */
8
+ const Form = ({
9
+ title = 'Form',
10
+ fields = [],
11
+ onSubmit,
12
+ submitButtonText = 'Submit'
13
+ }) => {
14
+ const [formData, setFormData] = useState(() => {
15
+ const initialData = {};
16
+ fields.forEach(field => {
17
+ initialData[field.name] = field.defaultValue || '';
18
+ });
19
+ return initialData;
20
+ });
21
+
22
+ const [errors, setErrors] = useState({});
23
+
24
+ const handleChange = (e) => {
25
+ const { name, value, type, checked } = e.target;
26
+ setFormData(prev => ({
27
+ ...prev,
28
+ [name]: type === 'checkbox' ? checked : value
29
+ }));
30
+
31
+ // Clear error when user starts typing
32
+ if (errors[name]) {
33
+ setErrors(prev => ({ ...prev, [name]: '' }));
34
+ }
35
+ };
36
+
37
+ const validateField = (field, value) => {
38
+ if (field.required && !value) {
39
+ return `${field.label} is required`;
40
+ }
41
+
42
+ if (field.pattern && value && !field.pattern.test(value)) {
43
+ return field.errorMessage || `Invalid ${field.label.toLowerCase()}`;
44
+ }
45
+
46
+ if (field.minLength && value.length < field.minLength) {
47
+ return `${field.label} must be at least ${field.minLength} characters`;
48
+ }
49
+
50
+ return '';
51
+ };
52
+
53
+ const handleSubmit = (e) => {
54
+ e.preventDefault();
55
+
56
+ const newErrors = {};
57
+ fields.forEach(field => {
58
+ const error = validateField(field, formData[field.name]);
59
+ if (error) {
60
+ newErrors[field.name] = error;
61
+ }
62
+ });
63
+
64
+ if (Object.keys(newErrors).length > 0) {
65
+ setErrors(newErrors);
66
+ return;
67
+ }
68
+
69
+ onSubmit?.(formData);
70
+ };
71
+
72
+ const renderField = (field) => {
73
+ const commonProps = {
74
+ id: field.name,
75
+ name: field.name,
76
+ value: formData[field.name] || '',
77
+ onChange: handleChange,
78
+ required: field.required,
79
+ placeholder: field.placeholder
80
+ };
81
+
82
+ switch (field.type) {
83
+ case 'textarea':
84
+ return <textarea {...commonProps} rows={field.rows || 4} />;
85
+ case 'select':
86
+ return (
87
+ <select {...commonProps}>
88
+ <option value="">Select...</option>
89
+ {field.options?.map(option => (
90
+ <option key={option.value} value={option.value}>
91
+ {option.label}
92
+ </option>
93
+ ))}
94
+ </select>
95
+ );
96
+ default:
97
+ return <input type={field.type || 'text'} {...commonProps} />;
98
+ }
99
+ };
100
+
101
+ return (
102
+ <div className="form-container">
103
+ <h2 className="form-title">{title}</h2>
104
+ <form onSubmit={handleSubmit} className="form">
105
+ {fields.map(field => (
106
+ <div key={field.name} className="form-group">
107
+ <label htmlFor={field.name} className="form-label">
108
+ {field.label}
109
+ {field.required && <span className="required">*</span>}
110
+ </label>
111
+ {renderField(field)}
112
+ {errors[field.name] && (
113
+ <span className="form-error">{errors[field.name]}</span>
114
+ )}
115
+ </div>
116
+ ))}
117
+ <button type="submit" className="form-submit">
118
+ {submitButtonText}
119
+ </button>
120
+ </form>
121
+ </div>
122
+ );
123
+ };
124
+
125
+ export default Form;
@@ -0,0 +1,113 @@
1
+ .input-wrapper {
2
+ margin-bottom: 1.5rem;
3
+ width: 100%;
4
+ }
5
+
6
+ .input-label {
7
+ display: block;
8
+ margin-bottom: 0.5rem;
9
+ font-size: 0.875rem;
10
+ font-weight: 600;
11
+ color: #374151;
12
+ }
13
+
14
+ .input-required {
15
+ color: #ef4444;
16
+ margin-left: 0.25rem;
17
+ }
18
+
19
+ .input-container {
20
+ position: relative;
21
+ display: flex;
22
+ align-items: center;
23
+ border: 2px solid #e5e7eb;
24
+ border-radius: 0.5rem;
25
+ background: white;
26
+ transition: all 0.2s ease;
27
+ }
28
+
29
+ .input-container:hover {
30
+ border-color: #d1d5db;
31
+ }
32
+
33
+ .input-container.focused {
34
+ border-color: ##PRIMARY_COLOR##;
35
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
36
+ }
37
+
38
+ .input-container.error {
39
+ border-color: #ef4444;
40
+ }
41
+
42
+ .input-container.error.focused {
43
+ box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1);
44
+ }
45
+
46
+ .input-container.disabled {
47
+ background: #f3f4f6;
48
+ cursor: not-allowed;
49
+ opacity: 0.6;
50
+ }
51
+
52
+ .input-icon {
53
+ padding: 0 0.75rem;
54
+ color: #6b7280;
55
+ display: flex;
56
+ align-items: center;
57
+ }
58
+
59
+ .input-field {
60
+ flex: 1;
61
+ padding: 0.75rem 1rem;
62
+ border: none;
63
+ outline: none;
64
+ font-size: 1rem;
65
+ background: transparent;
66
+ color: #1f2937;
67
+ }
68
+
69
+ .input-field::placeholder {
70
+ color: #9ca3af;
71
+ }
72
+
73
+ .input-field:disabled {
74
+ cursor: not-allowed;
75
+ }
76
+
77
+ .input-message {
78
+ margin-top: 0.5rem;
79
+ font-size: 0.875rem;
80
+ color: #6b7280;
81
+ }
82
+
83
+ .input-message.error {
84
+ color: #ef4444;
85
+ }
86
+
87
+ /* Dark mode support */
88
+ @media (prefers-color-scheme: dark) {
89
+ .input-label {
90
+ color: #e5e7eb;
91
+ }
92
+
93
+ .input-container {
94
+ background: #1f2937;
95
+ border-color: #374151;
96
+ }
97
+
98
+ .input-container:hover {
99
+ border-color: #4b5563;
100
+ }
101
+
102
+ .input-field {
103
+ color: #f3f4f6;
104
+ }
105
+
106
+ .input-field::placeholder {
107
+ color: #6b7280;
108
+ }
109
+
110
+ .input-container.disabled {
111
+ background: #111827;
112
+ }
113
+ }