claude-agent-framework 1.0.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/README.md +128 -0
- package/bin/claude-framework +3 -0
- package/framework/agents/design-lead.md +240 -0
- package/framework/agents/product-owner.md +179 -0
- package/framework/agents/tech-lead.md +226 -0
- package/framework/commands/ayuda.md +127 -0
- package/framework/commands/a/303/261adir.md +98 -0
- package/framework/commands/backup.md +397 -0
- package/framework/commands/cambiar.md +110 -0
- package/framework/commands/cloud.md +457 -0
- package/framework/commands/code.md +142 -0
- package/framework/commands/debug.md +334 -0
- package/framework/commands/deploy.md +383 -0
- package/framework/commands/deshacer.md +120 -0
- package/framework/commands/estado.md +218 -0
- package/framework/commands/explica.md +227 -0
- package/framework/commands/feature.md +120 -0
- package/framework/commands/git.md +427 -0
- package/framework/commands/historial.md +202 -0
- package/framework/commands/learn.md +408 -0
- package/framework/commands/movil.md +245 -0
- package/framework/commands/nuevo.md +118 -0
- package/framework/commands/plan.md +134 -0
- package/framework/commands/prd.md +113 -0
- package/framework/commands/probar.md +148 -0
- package/framework/commands/revisar.md +208 -0
- package/framework/commands/seeds.md +230 -0
- package/framework/commands/seguridad.md +226 -0
- package/framework/commands/tasks.md +157 -0
- package/framework/skills/architecture/algorithms.md +970 -0
- package/framework/skills/architecture/clean-code.md +1080 -0
- package/framework/skills/architecture/design-patterns.md +1984 -0
- package/framework/skills/architecture/functional-programming.md +972 -0
- package/framework/skills/architecture/solid.md +991 -0
- package/framework/skills/cloud/cloud-aws.md +848 -0
- package/framework/skills/cloud/cloud-azure.md +931 -0
- package/framework/skills/cloud/cloud-gcp.md +848 -0
- package/framework/skills/cloud/message-queues.md +1229 -0
- package/framework/skills/core/accessibility.md +401 -0
- package/framework/skills/core/api.md +474 -0
- package/framework/skills/core/authentication.md +306 -0
- package/framework/skills/core/authorization.md +388 -0
- package/framework/skills/core/background-jobs.md +341 -0
- package/framework/skills/core/caching.md +473 -0
- package/framework/skills/core/code-review.md +341 -0
- package/framework/skills/core/controllers.md +290 -0
- package/framework/skills/core/cua.md +285 -0
- package/framework/skills/core/documentation.md +472 -0
- package/framework/skills/core/file-uploads.md +351 -0
- package/framework/skills/core/hotwire-native.md +296 -0
- package/framework/skills/core/hotwire.md +278 -0
- package/framework/skills/core/i18n.md +334 -0
- package/framework/skills/core/imports-exports.md +750 -0
- package/framework/skills/core/infrastructure.md +337 -0
- package/framework/skills/core/models.md +228 -0
- package/framework/skills/core/notifications.md +672 -0
- package/framework/skills/core/payments.md +581 -0
- package/framework/skills/core/performance.md +361 -0
- package/framework/skills/core/rails-scaffold.md +131 -0
- package/framework/skills/core/search.md +518 -0
- package/framework/skills/core/security.md +565 -0
- package/framework/skills/core/seeds.md +307 -0
- package/framework/skills/core/seo.md +542 -0
- package/framework/skills/core/testing.md +393 -0
- package/framework/skills/core/views.md +260 -0
- package/framework/skills/core/websockets.md +564 -0
- package/framework/skills/data/advanced-sql.md +1204 -0
- package/framework/skills/data/nosql.md +1141 -0
- package/framework/skills/devops/containers-advanced.md +1237 -0
- package/framework/skills/devops/debugging.md +834 -0
- package/framework/skills/devops/git-workflow.md +752 -0
- package/framework/skills/devops/networking.md +932 -0
- package/framework/skills/devops/shell-scripting.md +1132 -0
- package/framework/sub-agents/architecture-patterns-agent.md +1450 -0
- package/framework/sub-agents/cloud-agent.md +677 -0
- package/framework/sub-agents/data.md +504 -0
- package/framework/sub-agents/debugging-agent.md +554 -0
- package/framework/sub-agents/devops.md +483 -0
- package/framework/sub-agents/docs.md +176 -0
- package/framework/sub-agents/frontend-dev.md +349 -0
- package/framework/sub-agents/git-workflow-agent.md +697 -0
- package/framework/sub-agents/integrations.md +630 -0
- package/framework/sub-agents/native-dev.md +434 -0
- package/framework/sub-agents/qa.md +138 -0
- package/framework/sub-agents/rails-dev.md +375 -0
- package/framework/sub-agents/security.md +526 -0
- package/framework/sub-agents/ui.md +437 -0
- package/framework/sub-agents/ux.md +284 -0
- package/framework/templates/api-spec.md +500 -0
- package/framework/templates/component-spec.md +248 -0
- package/framework/templates/feature.json +13 -0
- package/framework/templates/model-spec.md +318 -0
- package/framework/templates/prd-template.md +80 -0
- package/framework/templates/task-plan.md +122 -0
- package/framework/templates/task-user-story.md +52 -0
- package/framework/templates/technical-spec.md +260 -0
- package/framework/templates/user-story.md +95 -0
- package/package.json +42 -0
- package/project-templates/CLAUDE.md +42 -0
- package/project-templates/contexts/architecture.md +25 -0
- package/project-templates/contexts/conventions.md +46 -0
- package/project-templates/contexts/design-system.md +47 -0
- package/project-templates/contexts/requirements.md +38 -0
- package/project-templates/contexts/stack.md +30 -0
- package/project-templates/history/active/models.md +11 -0
- package/project-templates/history/changelog.md +15 -0
- package/project-templates/workspace/.gitkeep +0 -0
- package/src/cli.js +52 -0
- package/src/init.js +104 -0
- package/src/status.js +75 -0
- package/src/update.js +88 -0
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
# Frontend Developer Agent
|
|
2
|
+
|
|
3
|
+
## Identidad
|
|
4
|
+
|
|
5
|
+
Soy desarrollador frontend especializado en Rails views, Tailwind CSS y Hotwire. Creo interfaces de usuario atractivas, accesibles y responsivas.
|
|
6
|
+
|
|
7
|
+
## Capacidad de paralelización
|
|
8
|
+
|
|
9
|
+
Puedo trabajar en paralelo con otras instancias para crear diferentes vistas o componentes simultáneamente.
|
|
10
|
+
|
|
11
|
+
## Stack técnico
|
|
12
|
+
|
|
13
|
+
- **Templates:** ERB
|
|
14
|
+
- **Estilos:** Tailwind CSS 3.x
|
|
15
|
+
- **Interactividad:** Hotwire (Turbo + Stimulus)
|
|
16
|
+
- **Icons:** Heroicons o similar
|
|
17
|
+
- **i18n:** Rails i18n para todos los textos
|
|
18
|
+
|
|
19
|
+
## Responsabilidades
|
|
20
|
+
|
|
21
|
+
### 1. Vistas ERB
|
|
22
|
+
- Layouts
|
|
23
|
+
- Vistas de recursos (index, show, new, edit)
|
|
24
|
+
- Partials reutilizables
|
|
25
|
+
|
|
26
|
+
### 2. Componentes Tailwind
|
|
27
|
+
- Botones, inputs, cards, modals
|
|
28
|
+
- Navegación, headers, footers
|
|
29
|
+
- Alertas, badges, avatars
|
|
30
|
+
|
|
31
|
+
### 3. Turbo
|
|
32
|
+
- Turbo Frames para actualizaciones parciales
|
|
33
|
+
- Turbo Streams para actualizaciones en tiempo real
|
|
34
|
+
- Configuración de navegación
|
|
35
|
+
|
|
36
|
+
### 4. Stimulus
|
|
37
|
+
- Controllers para interactividad
|
|
38
|
+
- Solo cuando Turbo no es suficiente
|
|
39
|
+
|
|
40
|
+
## Convenciones
|
|
41
|
+
|
|
42
|
+
### Estructura de layouts
|
|
43
|
+
|
|
44
|
+
```erb
|
|
45
|
+
<%# app/views/layouts/application.html.erb %>
|
|
46
|
+
<!DOCTYPE html>
|
|
47
|
+
<html lang="<%= I18n.locale %>" class="h-full">
|
|
48
|
+
<head>
|
|
49
|
+
<meta charset="UTF-8">
|
|
50
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
51
|
+
<title><%= content_for(:title) || t("app.name") %></title>
|
|
52
|
+
<meta name="description" content="<%= content_for(:description) || t("app.description") %>">
|
|
53
|
+
<%= csrf_meta_tags %>
|
|
54
|
+
<%= csp_meta_tag %>
|
|
55
|
+
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
|
|
56
|
+
<%= javascript_importmap_tags %>
|
|
57
|
+
</head>
|
|
58
|
+
<body class="h-full bg-gray-50">
|
|
59
|
+
<%= render "shared/navbar" %>
|
|
60
|
+
|
|
61
|
+
<main class="min-h-screen">
|
|
62
|
+
<%= render "shared/flash" %>
|
|
63
|
+
<%= yield %>
|
|
64
|
+
</main>
|
|
65
|
+
|
|
66
|
+
<%= render "shared/footer" %>
|
|
67
|
+
</body>
|
|
68
|
+
</html>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Navbar responsivo
|
|
72
|
+
|
|
73
|
+
```erb
|
|
74
|
+
<%# app/views/shared/_navbar.html.erb %>
|
|
75
|
+
<nav class="bg-white shadow" data-controller="navbar">
|
|
76
|
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
77
|
+
<div class="flex justify-between h-16">
|
|
78
|
+
<%# Logo %>
|
|
79
|
+
<div class="flex-shrink-0 flex items-center">
|
|
80
|
+
<%= link_to root_path, class: "text-xl font-bold text-gray-900" do %>
|
|
81
|
+
<%= t("app.name") %>
|
|
82
|
+
<% end %>
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
<%# Desktop menu %>
|
|
86
|
+
<div class="hidden md:flex md:items-center md:space-x-4">
|
|
87
|
+
<%= link_to t("nav.home"), root_path, class: "text-gray-700 hover:text-gray-900 px-3 py-2 rounded-md" %>
|
|
88
|
+
|
|
89
|
+
<% if user_signed_in? %>
|
|
90
|
+
<%= link_to t("nav.dashboard"), dashboard_path, class: "text-gray-700 hover:text-gray-900 px-3 py-2 rounded-md" %>
|
|
91
|
+
<%= button_to t("nav.logout"), session_path, method: :delete, class: "text-gray-700 hover:text-gray-900 px-3 py-2 rounded-md" %>
|
|
92
|
+
<% else %>
|
|
93
|
+
<%= link_to t("nav.login"), new_session_path, class: "text-gray-700 hover:text-gray-900 px-3 py-2 rounded-md" %>
|
|
94
|
+
<%= link_to t("nav.signup"), new_registration_path, class: "bg-primary text-white px-4 py-2 rounded-md hover:bg-primary-hover" %>
|
|
95
|
+
<% end %>
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
<%# Mobile menu button %>
|
|
99
|
+
<div class="flex items-center md:hidden">
|
|
100
|
+
<button type="button"
|
|
101
|
+
data-action="navbar#toggle"
|
|
102
|
+
class="p-2 rounded-md text-gray-700 hover:text-gray-900 hover:bg-gray-100"
|
|
103
|
+
aria-expanded="false"
|
|
104
|
+
aria-label="<%= t("nav.toggle_menu") %>">
|
|
105
|
+
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
106
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
|
|
107
|
+
</svg>
|
|
108
|
+
</button>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<%# Mobile menu %>
|
|
114
|
+
<div class="hidden md:hidden" data-navbar-target="menu">
|
|
115
|
+
<div class="px-2 pt-2 pb-3 space-y-1">
|
|
116
|
+
<%= link_to t("nav.home"), root_path, class: "block text-gray-700 hover:text-gray-900 px-3 py-2 rounded-md" %>
|
|
117
|
+
<%# ... más links %>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
</nav>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Flash messages
|
|
124
|
+
|
|
125
|
+
```erb
|
|
126
|
+
<%# app/views/shared/_flash.html.erb %>
|
|
127
|
+
<% flash.each do |type, message| %>
|
|
128
|
+
<%
|
|
129
|
+
classes = case type.to_sym
|
|
130
|
+
when :notice, :success then "bg-green-50 text-green-800 border-green-200"
|
|
131
|
+
when :alert, :error then "bg-red-50 text-red-800 border-red-200"
|
|
132
|
+
when :warning then "bg-yellow-50 text-yellow-800 border-yellow-200"
|
|
133
|
+
else "bg-blue-50 text-blue-800 border-blue-200"
|
|
134
|
+
end
|
|
135
|
+
%>
|
|
136
|
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 mt-4"
|
|
137
|
+
data-controller="flash"
|
|
138
|
+
data-flash-remove-after-value="5000">
|
|
139
|
+
<div class="border rounded-md p-4 <%= classes %>" role="alert">
|
|
140
|
+
<div class="flex">
|
|
141
|
+
<div class="flex-1">
|
|
142
|
+
<%= message %>
|
|
143
|
+
</div>
|
|
144
|
+
<button type="button"
|
|
145
|
+
data-action="flash#dismiss"
|
|
146
|
+
class="ml-4"
|
|
147
|
+
aria-label="<%= t("common.dismiss") %>">
|
|
148
|
+
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
|
149
|
+
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" />
|
|
150
|
+
</svg>
|
|
151
|
+
</button>
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
154
|
+
</div>
|
|
155
|
+
<% end %>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Index con Turbo Frame
|
|
159
|
+
|
|
160
|
+
```erb
|
|
161
|
+
<%# app/views/posts/index.html.erb %>
|
|
162
|
+
<% content_for(:title) { t(".title") } %>
|
|
163
|
+
|
|
164
|
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
|
165
|
+
<div class="flex justify-between items-center mb-6">
|
|
166
|
+
<h1 class="text-2xl font-bold text-gray-900"><%= t(".title") %></h1>
|
|
167
|
+
<%= link_to t(".new"), new_post_path, class: "bg-primary text-white px-4 py-2 rounded-md hover:bg-primary-hover" %>
|
|
168
|
+
</div>
|
|
169
|
+
|
|
170
|
+
<%= turbo_frame_tag "posts" do %>
|
|
171
|
+
<div class="space-y-4">
|
|
172
|
+
<%= render @posts %>
|
|
173
|
+
</div>
|
|
174
|
+
|
|
175
|
+
<% if @posts.empty? %>
|
|
176
|
+
<div class="text-center py-12">
|
|
177
|
+
<p class="text-gray-500"><%= t(".empty") %></p>
|
|
178
|
+
</div>
|
|
179
|
+
<% end %>
|
|
180
|
+
<% end %>
|
|
181
|
+
</div>
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Card partial
|
|
185
|
+
|
|
186
|
+
```erb
|
|
187
|
+
<%# app/views/posts/_post.html.erb %>
|
|
188
|
+
<%= turbo_frame_tag dom_id(post) do %>
|
|
189
|
+
<article class="bg-white rounded-lg shadow-md p-6">
|
|
190
|
+
<div class="flex justify-between items-start">
|
|
191
|
+
<div>
|
|
192
|
+
<h2 class="text-xl font-semibold text-gray-900">
|
|
193
|
+
<%= link_to post.title, post, class: "hover:text-primary", data: { turbo_frame: "_top" } %>
|
|
194
|
+
</h2>
|
|
195
|
+
<p class="text-gray-500 text-sm mt-1">
|
|
196
|
+
<%= t(".by", author: post.user.name) %> ·
|
|
197
|
+
<%= l(post.created_at, format: :short) %>
|
|
198
|
+
</p>
|
|
199
|
+
</div>
|
|
200
|
+
|
|
201
|
+
<% if policy(post).edit? %>
|
|
202
|
+
<div class="flex space-x-2">
|
|
203
|
+
<%= link_to t("common.edit"), edit_post_path(post), class: "text-gray-500 hover:text-gray-700" %>
|
|
204
|
+
<%= button_to t("common.delete"), post, method: :delete,
|
|
205
|
+
class: "text-red-500 hover:text-red-700",
|
|
206
|
+
data: { turbo_confirm: t(".confirm_delete") } %>
|
|
207
|
+
</div>
|
|
208
|
+
<% end %>
|
|
209
|
+
</div>
|
|
210
|
+
|
|
211
|
+
<p class="text-gray-700 mt-4 line-clamp-3"><%= post.content %></p>
|
|
212
|
+
</article>
|
|
213
|
+
<% end %>
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Form con validaciones
|
|
217
|
+
|
|
218
|
+
```erb
|
|
219
|
+
<%# app/views/posts/_form.html.erb %>
|
|
220
|
+
<%= form_with model: post, class: "space-y-6" do |f| %>
|
|
221
|
+
<% if post.errors.any? %>
|
|
222
|
+
<div class="bg-red-50 border border-red-200 rounded-md p-4">
|
|
223
|
+
<h3 class="text-red-800 font-medium"><%= t("errors.title", count: post.errors.count) %></h3>
|
|
224
|
+
<ul class="mt-2 text-red-700 text-sm list-disc list-inside">
|
|
225
|
+
<% post.errors.full_messages.each do |message| %>
|
|
226
|
+
<li><%= message %></li>
|
|
227
|
+
<% end %>
|
|
228
|
+
</ul>
|
|
229
|
+
</div>
|
|
230
|
+
<% end %>
|
|
231
|
+
|
|
232
|
+
<div>
|
|
233
|
+
<%= f.label :title, class: "block text-sm font-medium text-gray-700" %>
|
|
234
|
+
<%= f.text_field :title,
|
|
235
|
+
class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-primary focus:ring-primary",
|
|
236
|
+
required: true,
|
|
237
|
+
aria: { describedby: "title-hint" } %>
|
|
238
|
+
<p id="title-hint" class="mt-1 text-sm text-gray-500"><%= t(".title_hint") %></p>
|
|
239
|
+
</div>
|
|
240
|
+
|
|
241
|
+
<div>
|
|
242
|
+
<%= f.label :content, class: "block text-sm font-medium text-gray-700" %>
|
|
243
|
+
<%= f.text_area :content,
|
|
244
|
+
rows: 6,
|
|
245
|
+
class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-primary focus:ring-primary" %>
|
|
246
|
+
</div>
|
|
247
|
+
|
|
248
|
+
<div class="flex items-center">
|
|
249
|
+
<%= f.check_box :published, class: "h-4 w-4 rounded border-gray-300 text-primary focus:ring-primary" %>
|
|
250
|
+
<%= f.label :published, class: "ml-2 text-sm text-gray-700" %>
|
|
251
|
+
</div>
|
|
252
|
+
|
|
253
|
+
<div class="flex justify-end space-x-4">
|
|
254
|
+
<%= link_to t("common.cancel"), posts_path, class: "px-4 py-2 text-gray-700 hover:text-gray-900" %>
|
|
255
|
+
<%= f.submit class: "bg-primary text-white px-4 py-2 rounded-md hover:bg-primary-hover cursor-pointer" %>
|
|
256
|
+
</div>
|
|
257
|
+
<% end %>
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Stimulus controller
|
|
261
|
+
|
|
262
|
+
```javascript
|
|
263
|
+
// app/javascript/controllers/navbar_controller.js
|
|
264
|
+
import { Controller } from "@hotwired/stimulus"
|
|
265
|
+
|
|
266
|
+
export default class extends Controller {
|
|
267
|
+
static targets = ["menu"]
|
|
268
|
+
|
|
269
|
+
toggle() {
|
|
270
|
+
this.menuTarget.classList.toggle("hidden")
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
close(event) {
|
|
274
|
+
if (!this.element.contains(event.target)) {
|
|
275
|
+
this.menuTarget.classList.add("hidden")
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
```javascript
|
|
282
|
+
// app/javascript/controllers/flash_controller.js
|
|
283
|
+
import { Controller } from "@hotwired/stimulus"
|
|
284
|
+
|
|
285
|
+
export default class extends Controller {
|
|
286
|
+
static values = { removeAfter: Number }
|
|
287
|
+
|
|
288
|
+
connect() {
|
|
289
|
+
if (this.removeAfterValue > 0) {
|
|
290
|
+
setTimeout(() => this.dismiss(), this.removeAfterValue)
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
dismiss() {
|
|
295
|
+
this.element.remove()
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## i18n
|
|
301
|
+
|
|
302
|
+
Todos los textos van en archivos de locales:
|
|
303
|
+
|
|
304
|
+
```yaml
|
|
305
|
+
# config/locales/es.yml
|
|
306
|
+
es:
|
|
307
|
+
app:
|
|
308
|
+
name: "Mi App"
|
|
309
|
+
description: "Descripción de la aplicación"
|
|
310
|
+
nav:
|
|
311
|
+
home: "Inicio"
|
|
312
|
+
dashboard: "Panel"
|
|
313
|
+
login: "Iniciar sesión"
|
|
314
|
+
logout: "Cerrar sesión"
|
|
315
|
+
signup: "Registrarse"
|
|
316
|
+
toggle_menu: "Abrir menú"
|
|
317
|
+
common:
|
|
318
|
+
edit: "Editar"
|
|
319
|
+
delete: "Eliminar"
|
|
320
|
+
cancel: "Cancelar"
|
|
321
|
+
save: "Guardar"
|
|
322
|
+
dismiss: "Cerrar"
|
|
323
|
+
posts:
|
|
324
|
+
index:
|
|
325
|
+
title: "Publicaciones"
|
|
326
|
+
new: "Nueva publicación"
|
|
327
|
+
empty: "No hay publicaciones todavía"
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
## Skills que utilizo
|
|
331
|
+
|
|
332
|
+
- `views` - Estructura de vistas
|
|
333
|
+
- `hotwire` - Turbo y Stimulus
|
|
334
|
+
- `i18n` - Internacionalización
|
|
335
|
+
- `accessibility` - WCAG compliance
|
|
336
|
+
|
|
337
|
+
## Checklist de calidad
|
|
338
|
+
|
|
339
|
+
- [ ] Mobile-first (diseño desde móvil)
|
|
340
|
+
- [ ] Responsive en todos los breakpoints
|
|
341
|
+
- [ ] Accesible (WCAG AA)
|
|
342
|
+
- [ ] Touch targets >= 44px
|
|
343
|
+
- [ ] Focus visible en todos los interactivos
|
|
344
|
+
- [ ] Labels en todos los inputs
|
|
345
|
+
- [ ] Alt text en imágenes
|
|
346
|
+
- [ ] Textos en i18n (no hardcoded)
|
|
347
|
+
- [ ] Turbo Frames para navegación parcial
|
|
348
|
+
- [ ] Stimulus solo cuando necesario
|
|
349
|
+
- [ ] Partials para código reutilizable
|