codymaster 4.4.5 → 4.5.1
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 +33 -0
- package/README.md +29 -14
- package/commands/demo.md +1 -1
- package/dist/context-bus.js +70 -0
- package/dist/context-db.js +265 -0
- package/dist/continuity.js +12 -0
- package/dist/file-watcher.js +79 -0
- package/dist/index.js +152 -1
- package/dist/l0-indexer.js +158 -0
- package/dist/mcp-context-server.js +400 -0
- package/dist/migrate-json-to-sqlite.js +126 -0
- package/dist/skill-chain.js +19 -3
- package/dist/token-budget.js +108 -0
- package/dist/uri-resolver.js +203 -0
- package/package.json +4 -1
- package/skills/_shared/helpers.md +50 -14
- package/skills/cm-autopilot/SKILL.md +29 -0
- package/skills/cm-autopilot/scripts/autopilot.py +190 -0
- package/skills/cm-continuity/SKILL.md +90 -28
- package/skills/cm-skill-chain/SKILL.md +47 -1
- package/skills/cm-start/SKILL.md +11 -2
- package/skills/boxme-git-config/SKILL.md +0 -56
- package/skills/boxme-local-dev/SKILL.md +0 -66
- package/skills/jobs-to-be-done/SKILL.md +0 -266
- package/skills/jobs-to-be-done/references/case-studies.md +0 -154
- package/skills/jobs-to-be-done/references/competitive-strategy.md +0 -280
- package/skills/jobs-to-be-done/references/diagnostics.md +0 -158
- package/skills/jobs-to-be-done/references/innovation-process.md +0 -392
- package/skills/jobs-to-be-done/references/organizational-change.md +0 -328
- package/skills/marketplace-report-crawler/SKILL.md +0 -176
- package/skills/marketplace-report-crawler/config/accounts.json +0 -41
- package/skills/marketplace-report-crawler/config/report-types.json +0 -422
- package/skills/marketplace-report-crawler/config/sessions.json +0 -3
- package/skills/marketplace-report-crawler/scripts/ab-wrapper.sh +0 -102
- package/skills/marketplace-report-crawler/scripts/browser-actions/lazada/lazada-actions.js +0 -114
- package/skills/marketplace-report-crawler/scripts/browser-actions/shopee/shopee-actions.js +0 -94
- package/skills/marketplace-report-crawler/scripts/browser-actions/tiktok/tiktok-actions.js +0 -272
- package/skills/marketplace-report-crawler/scripts/crawl-runner.js +0 -281
- package/skills/marketplace-report-crawler/scripts/session-check.sh +0 -72
- package/skills/marketplace-report-crawler/scripts/session-manager.sh +0 -349
- package/skills/marketplace-report-crawler/scripts/setup-folders.sh +0 -83
- package/skills/medical-research/SKILL.md +0 -194
- package/skills/medical-research/scripts/evidence_checker.py +0 -288
- package/skills/mom-test/SKILL.md +0 -267
- package/skills/mom-test/references/avoiding-bad-data.md +0 -221
- package/skills/mom-test/references/case-studies.md +0 -306
- package/skills/mom-test/references/commitment-advancement.md +0 -219
- package/skills/mom-test/references/finding-conversations.md +0 -251
- package/skills/mom-test/references/processing-learning.md +0 -256
- package/skills/mom-test/references/question-patterns.md +0 -198
- package/skills/pandasai-analytics/SKILL.md +0 -251
- package/skills/release-it/SKILL.md +0 -235
- package/skills/release-it/references/anti-patterns.md +0 -279
- package/skills/release-it/references/capacity-planning.md +0 -285
- package/skills/release-it/references/chaos-engineering.md +0 -325
- package/skills/release-it/references/deployment-strategies.md +0 -331
- package/skills/release-it/references/observability.md +0 -301
- package/skills/release-it/references/stability-patterns.md +0 -355
- package/skills/skill-creator-ultra/.agents/workflows/skill-audit.md +0 -37
- package/skills/skill-creator-ultra/.agents/workflows/skill-compare.md +0 -34
- package/skills/skill-creator-ultra/.agents/workflows/skill-export.md +0 -51
- package/skills/skill-creator-ultra/.agents/workflows/skill-generate.md +0 -39
- package/skills/skill-creator-ultra/.agents/workflows/skill-scaffold.md +0 -52
- package/skills/skill-creator-ultra/.agents/workflows/skill-simulate.md +0 -25
- package/skills/skill-creator-ultra/.agents/workflows/skill-stats.md +0 -31
- package/skills/skill-creator-ultra/.agents/workflows/skill-validate.md +0 -25
- package/skills/skill-creator-ultra/README.md +0 -1242
- package/skills/skill-creator-ultra/SKILL.md +0 -388
- package/skills/skill-creator-ultra/agents/analyzer.md +0 -274
- package/skills/skill-creator-ultra/agents/comparator.md +0 -202
- package/skills/skill-creator-ultra/agents/grader.md +0 -223
- package/skills/skill-creator-ultra/assets/eval_review.html +0 -146
- package/skills/skill-creator-ultra/eval-viewer/generate_review.py +0 -471
- package/skills/skill-creator-ultra/eval-viewer/viewer.html +0 -1325
- package/skills/skill-creator-ultra/examples/example_anthropic_frontend.md +0 -109
- package/skills/skill-creator-ultra/examples/example_anthropic_pdf.md +0 -116
- package/skills/skill-creator-ultra/examples/example_api_docs.md +0 -189
- package/skills/skill-creator-ultra/examples/example_db_migration.md +0 -253
- package/skills/skill-creator-ultra/examples/example_git_commit.md +0 -111
- package/skills/skill-creator-ultra/install.ps1 +0 -289
- package/skills/skill-creator-ultra/install.sh +0 -313
- package/skills/skill-creator-ultra/phases/phase1_interview.md +0 -202
- package/skills/skill-creator-ultra/phases/phase2_extract.md +0 -55
- package/skills/skill-creator-ultra/phases/phase3_detect.md +0 -57
- package/skills/skill-creator-ultra/phases/phase4_generate.md +0 -543
- package/skills/skill-creator-ultra/phases/phase5_test.md +0 -319
- package/skills/skill-creator-ultra/phases/phase6_eval.md +0 -301
- package/skills/skill-creator-ultra/phases/phase7_iterate.md +0 -103
- package/skills/skill-creator-ultra/phases/phase8_optimize.md +0 -113
- package/skills/skill-creator-ultra/resources/advanced_patterns.md +0 -499
- package/skills/skill-creator-ultra/resources/anti_patterns.md +0 -376
- package/skills/skill-creator-ultra/resources/blueprints.md +0 -498
- package/skills/skill-creator-ultra/resources/checklist.md +0 -243
- package/skills/skill-creator-ultra/resources/composition_cookbook.md +0 -291
- package/skills/skill-creator-ultra/resources/description_optimization.md +0 -90
- package/skills/skill-creator-ultra/resources/eval_guide.md +0 -133
- package/skills/skill-creator-ultra/resources/industry_questions.md +0 -189
- package/skills/skill-creator-ultra/resources/interview_questions.md +0 -200
- package/skills/skill-creator-ultra/resources/pattern_detection.md +0 -200
- package/skills/skill-creator-ultra/resources/prompt_engineering.md +0 -531
- package/skills/skill-creator-ultra/resources/schemas.md +0 -430
- package/skills/skill-creator-ultra/resources/script_integration.md +0 -593
- package/skills/skill-creator-ultra/resources/scripts_guide.md +0 -339
- package/skills/skill-creator-ultra/resources/skill_template.md +0 -124
- package/skills/skill-creator-ultra/resources/skill_writing_guide.md +0 -634
- package/skills/skill-creator-ultra/resources/versioning_guide.md +0 -193
- package/skills/skill-creator-ultra/scripts/ci_eval.py +0 -200
- package/skills/skill-creator-ultra/scripts/package_skill.py +0 -165
- package/skills/skill-creator-ultra/scripts/simulate_skill.py +0 -398
- package/skills/skill-creator-ultra/scripts/skill_audit.py +0 -611
- package/skills/skill-creator-ultra/scripts/skill_compare.py +0 -265
- package/skills/skill-creator-ultra/scripts/skill_export.py +0 -334
- package/skills/skill-creator-ultra/scripts/skill_scaffold.py +0 -403
- package/skills/skill-creator-ultra/scripts/skill_stats.py +0 -339
- package/skills/skill-creator-ultra/scripts/validate_skill.py +0 -411
- package/skills/tailwind-mastery/SKILL.md +0 -229
- package/skills/vercel-react-best-practices/AGENTS.md +0 -3373
- package/skills/vercel-react-best-practices/README.md +0 -123
- package/skills/vercel-react-best-practices/SKILL.md +0 -143
- package/skills/vercel-react-best-practices/rules/_sections.md +0 -46
- package/skills/vercel-react-best-practices/rules/_template.md +0 -28
- package/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +0 -55
- package/skills/vercel-react-best-practices/rules/advanced-init-once.md +0 -42
- package/skills/vercel-react-best-practices/rules/advanced-use-latest.md +0 -39
- package/skills/vercel-react-best-practices/rules/async-api-routes.md +0 -38
- package/skills/vercel-react-best-practices/rules/async-defer-await.md +0 -80
- package/skills/vercel-react-best-practices/rules/async-dependencies.md +0 -51
- package/skills/vercel-react-best-practices/rules/async-parallel.md +0 -28
- package/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +0 -99
- package/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +0 -59
- package/skills/vercel-react-best-practices/rules/bundle-conditional.md +0 -31
- package/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +0 -49
- package/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +0 -35
- package/skills/vercel-react-best-practices/rules/bundle-preload.md +0 -50
- package/skills/vercel-react-best-practices/rules/client-event-listeners.md +0 -74
- package/skills/vercel-react-best-practices/rules/client-localstorage-schema.md +0 -71
- package/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md +0 -48
- package/skills/vercel-react-best-practices/rules/client-swr-dedup.md +0 -56
- package/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +0 -107
- package/skills/vercel-react-best-practices/rules/js-cache-function-results.md +0 -80
- package/skills/vercel-react-best-practices/rules/js-cache-property-access.md +0 -28
- package/skills/vercel-react-best-practices/rules/js-cache-storage.md +0 -70
- package/skills/vercel-react-best-practices/rules/js-combine-iterations.md +0 -32
- package/skills/vercel-react-best-practices/rules/js-early-exit.md +0 -50
- package/skills/vercel-react-best-practices/rules/js-flatmap-filter.md +0 -60
- package/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +0 -45
- package/skills/vercel-react-best-practices/rules/js-index-maps.md +0 -37
- package/skills/vercel-react-best-practices/rules/js-length-check-first.md +0 -49
- package/skills/vercel-react-best-practices/rules/js-min-max-loop.md +0 -82
- package/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +0 -24
- package/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +0 -57
- package/skills/vercel-react-best-practices/rules/rendering-activity.md +0 -26
- package/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +0 -47
- package/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +0 -40
- package/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +0 -38
- package/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +0 -46
- package/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +0 -82
- package/skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md +0 -30
- package/skills/vercel-react-best-practices/rules/rendering-resource-hints.md +0 -85
- package/skills/vercel-react-best-practices/rules/rendering-script-defer-async.md +0 -68
- package/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +0 -28
- package/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md +0 -75
- package/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +0 -39
- package/skills/vercel-react-best-practices/rules/rerender-dependencies.md +0 -45
- package/skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md +0 -40
- package/skills/vercel-react-best-practices/rules/rerender-derived-state.md +0 -29
- package/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +0 -74
- package/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +0 -58
- package/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md +0 -38
- package/skills/vercel-react-best-practices/rules/rerender-memo.md +0 -44
- package/skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md +0 -45
- package/skills/vercel-react-best-practices/rules/rerender-no-inline-components.md +0 -82
- package/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +0 -35
- package/skills/vercel-react-best-practices/rules/rerender-split-combined-hooks.md +0 -64
- package/skills/vercel-react-best-practices/rules/rerender-transitions.md +0 -40
- package/skills/vercel-react-best-practices/rules/rerender-use-deferred-value.md +0 -59
- package/skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md +0 -73
- package/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +0 -73
- package/skills/vercel-react-best-practices/rules/server-auth-actions.md +0 -96
- package/skills/vercel-react-best-practices/rules/server-cache-lru.md +0 -41
- package/skills/vercel-react-best-practices/rules/server-cache-react.md +0 -76
- package/skills/vercel-react-best-practices/rules/server-dedup-props.md +0 -65
- package/skills/vercel-react-best-practices/rules/server-hoist-static-io.md +0 -142
- package/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +0 -83
- package/skills/vercel-react-best-practices/rules/server-serialization.md +0 -38
- package/skills/web-design-guidelines/SKILL.md +0 -39
|
@@ -1,411 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
validate_skill.py — Kiểm tra tính hợp lệ của file SKILL.md
|
|
4
|
-
|
|
5
|
-
Sử dụng:
|
|
6
|
-
python validate_skill.py /path/to/SKILL.md
|
|
7
|
-
python validate_skill.py /path/to/skill-folder/
|
|
8
|
-
|
|
9
|
-
Script sẽ kiểm tra:
|
|
10
|
-
1. YAML frontmatter có hợp lệ không
|
|
11
|
-
2. Có đầy đủ các section bắt buộc không (Goal, Instructions)
|
|
12
|
-
3. Có Examples không (khuyến khích mạnh)
|
|
13
|
-
4. Có Constraints không (khuyến khích)
|
|
14
|
-
5. Description có đủ chi tiết không
|
|
15
|
-
6. Cấu trúc thư mục có chuẩn không
|
|
16
|
-
"""
|
|
17
|
-
|
|
18
|
-
import sys
|
|
19
|
-
import os
|
|
20
|
-
import re
|
|
21
|
-
from pathlib import Path
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
# ============================================================
|
|
25
|
-
# CONSTANTS
|
|
26
|
-
# ============================================================
|
|
27
|
-
|
|
28
|
-
REQUIRED_SECTIONS = ['goal', 'instructions']
|
|
29
|
-
RECOMMENDED_SECTIONS = ['examples', 'constraints']
|
|
30
|
-
MIN_DESCRIPTION_LENGTH = 30 # Ký tự tối thiểu cho description
|
|
31
|
-
MIN_DESCRIPTION_WORDS = 5 # Từ tối thiểu cho description
|
|
32
|
-
MAX_SKILL_NAME_LENGTH = 50 # Ký tự tối đa cho tên skill
|
|
33
|
-
VALID_SUBFOLDERS = {'scripts', 'resources', 'examples'}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
# ============================================================
|
|
37
|
-
# COLOR OUTPUT (Windows + Unix compatible)
|
|
38
|
-
# ============================================================
|
|
39
|
-
|
|
40
|
-
class Colors:
|
|
41
|
-
"""ANSI color codes cho terminal output."""
|
|
42
|
-
PASS = '\033[92m' # Green
|
|
43
|
-
FAIL = '\033[91m' # Red
|
|
44
|
-
WARN = '\033[93m' # Yellow
|
|
45
|
-
INFO = '\033[96m' # Cyan
|
|
46
|
-
BOLD = '\033[1m'
|
|
47
|
-
RESET = '\033[0m'
|
|
48
|
-
|
|
49
|
-
@staticmethod
|
|
50
|
-
def enable_windows():
|
|
51
|
-
"""Bật ANSI colors cho Windows terminal."""
|
|
52
|
-
if os.name == 'nt':
|
|
53
|
-
try:
|
|
54
|
-
import ctypes
|
|
55
|
-
kernel32 = ctypes.windll.kernel32
|
|
56
|
-
kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
|
|
57
|
-
except Exception:
|
|
58
|
-
pass
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def print_result(status, message):
|
|
62
|
-
"""In kết quả kiểm tra với màu sắc."""
|
|
63
|
-
if status == 'PASS':
|
|
64
|
-
icon = f"{Colors.PASS}✅ PASS{Colors.RESET}"
|
|
65
|
-
elif status == 'FAIL':
|
|
66
|
-
icon = f"{Colors.FAIL}❌ FAIL{Colors.RESET}"
|
|
67
|
-
elif status == 'WARN':
|
|
68
|
-
icon = f"{Colors.WARN}⚠️ WARN{Colors.RESET}"
|
|
69
|
-
else:
|
|
70
|
-
icon = f"{Colors.INFO}ℹ️ INFO{Colors.RESET}"
|
|
71
|
-
|
|
72
|
-
print(f" {icon} {message}")
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
# ============================================================
|
|
76
|
-
# YAML FRONTMATTER PARSER
|
|
77
|
-
# ============================================================
|
|
78
|
-
|
|
79
|
-
def parse_frontmatter(content):
|
|
80
|
-
"""
|
|
81
|
-
Trích xuất YAML frontmatter từ nội dung SKILL.md.
|
|
82
|
-
|
|
83
|
-
Returns:
|
|
84
|
-
tuple: (frontmatter_dict, body_content) hoặc (None, content) nếu không có frontmatter
|
|
85
|
-
"""
|
|
86
|
-
# Tìm YAML frontmatter (giữa 2 dấu ---)
|
|
87
|
-
pattern = r'^---\s*\n(.*?)\n---\s*\n(.*)$'
|
|
88
|
-
match = re.match(pattern, content, re.DOTALL)
|
|
89
|
-
|
|
90
|
-
if not match:
|
|
91
|
-
return None, content
|
|
92
|
-
|
|
93
|
-
yaml_content = match.group(1)
|
|
94
|
-
body = match.group(2)
|
|
95
|
-
|
|
96
|
-
# Parse YAML đơn giản (hỗ trợ multi-line block scalar `|` và `>`)
|
|
97
|
-
frontmatter = {}
|
|
98
|
-
lines = yaml_content.strip().split('\n')
|
|
99
|
-
i = 0
|
|
100
|
-
while i < len(lines):
|
|
101
|
-
line = lines[i]
|
|
102
|
-
stripped = line.strip()
|
|
103
|
-
if ':' in stripped and not stripped.startswith('#'):
|
|
104
|
-
key, _, value = stripped.partition(':')
|
|
105
|
-
key = key.strip()
|
|
106
|
-
value = value.strip().strip('"').strip("'")
|
|
107
|
-
|
|
108
|
-
# Xử lý multi-line block scalar (| hoặc >)
|
|
109
|
-
if value in ('|', '>', '|+', '|-', '>+', '>-'):
|
|
110
|
-
multi_lines = []
|
|
111
|
-
i += 1
|
|
112
|
-
while i < len(lines):
|
|
113
|
-
next_line = lines[i]
|
|
114
|
-
# Dòng tiếp theo phải có indent (bắt đầu bằng space/tab)
|
|
115
|
-
if next_line and (next_line[0] == ' ' or next_line[0] == '\t'):
|
|
116
|
-
multi_lines.append(next_line.strip())
|
|
117
|
-
i += 1
|
|
118
|
-
else:
|
|
119
|
-
break
|
|
120
|
-
value = ' '.join(multi_lines)
|
|
121
|
-
|
|
122
|
-
frontmatter[key] = value
|
|
123
|
-
i += 1
|
|
124
|
-
|
|
125
|
-
return frontmatter, body
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
def find_sections(body):
|
|
129
|
-
"""
|
|
130
|
-
Tìm tất cả heading sections (# Level 1) trong body.
|
|
131
|
-
Bỏ qua headings nằm trong fenced code blocks (``` ... ```).
|
|
132
|
-
|
|
133
|
-
Returns:
|
|
134
|
-
dict: {section_name_lowercase: section_content}
|
|
135
|
-
"""
|
|
136
|
-
sections = {}
|
|
137
|
-
current_section = None
|
|
138
|
-
current_content = []
|
|
139
|
-
in_code_block = False
|
|
140
|
-
|
|
141
|
-
for line in body.split('\n'):
|
|
142
|
-
stripped = line.strip()
|
|
143
|
-
|
|
144
|
-
# Phát hiện bắt đầu/kết thúc code block
|
|
145
|
-
if stripped.startswith('```'):
|
|
146
|
-
in_code_block = not in_code_block
|
|
147
|
-
current_content.append(line)
|
|
148
|
-
continue
|
|
149
|
-
|
|
150
|
-
# Bỏ qua headings trong code block
|
|
151
|
-
if in_code_block:
|
|
152
|
-
current_content.append(line)
|
|
153
|
-
continue
|
|
154
|
-
|
|
155
|
-
# Tìm heading level 1 (# Title)
|
|
156
|
-
heading_match = re.match(r'^#\s+(.+)$', stripped)
|
|
157
|
-
if heading_match:
|
|
158
|
-
# Lưu section trước
|
|
159
|
-
if current_section:
|
|
160
|
-
sections[current_section] = '\n'.join(current_content)
|
|
161
|
-
current_section = heading_match.group(1).strip().lower()
|
|
162
|
-
current_content = []
|
|
163
|
-
else:
|
|
164
|
-
current_content.append(line)
|
|
165
|
-
|
|
166
|
-
# Lưu section cuối cùng
|
|
167
|
-
if current_section:
|
|
168
|
-
sections[current_section] = '\n'.join(current_content)
|
|
169
|
-
|
|
170
|
-
return sections
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
def count_examples(body):
|
|
174
|
-
"""Đếm số ví dụ (heading ## Ví dụ hoặc ## Example). Bỏ qua code blocks."""
|
|
175
|
-
# Xóa nội dung trong code blocks trước khi đếm
|
|
176
|
-
clean_body = re.sub(r'```.*?```', '', body, flags=re.DOTALL)
|
|
177
|
-
pattern = r'^##\s+(?:Ví dụ|Example)\s*\d*'
|
|
178
|
-
return len(re.findall(pattern, clean_body, re.MULTILINE | re.IGNORECASE))
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
def count_constraints(body):
|
|
182
|
-
"""Đếm số constraint (dòng bắt đầu bằng - KHÔNG ĐƯỢC hoặc - LUÔN LUÔN)."""
|
|
183
|
-
pattern = r'^[-*]\s*(?:KHÔNG ĐƯỢC|LUÔN LUÔN|NEVER|ALWAYS|🚫|⚠️)'
|
|
184
|
-
return len(re.findall(pattern, body, re.MULTILINE | re.IGNORECASE))
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
# ============================================================
|
|
188
|
-
# VALIDATORS
|
|
189
|
-
# ============================================================
|
|
190
|
-
|
|
191
|
-
def validate_frontmatter(frontmatter):
|
|
192
|
-
"""Kiểm tra YAML frontmatter."""
|
|
193
|
-
results = []
|
|
194
|
-
|
|
195
|
-
if frontmatter is None:
|
|
196
|
-
results.append(('FAIL', 'Không tìm thấy YAML frontmatter (--- đầu và cuối)'))
|
|
197
|
-
return results, False
|
|
198
|
-
|
|
199
|
-
results.append(('PASS', 'YAML frontmatter hợp lệ'))
|
|
200
|
-
|
|
201
|
-
# Check description (BẮT BUỘC)
|
|
202
|
-
desc = frontmatter.get('description', '')
|
|
203
|
-
if not desc:
|
|
204
|
-
results.append(('FAIL', 'Thiếu trường `description` — Đây là phần QUAN TRỌNG NHẤT'))
|
|
205
|
-
elif len(desc) < MIN_DESCRIPTION_LENGTH:
|
|
206
|
-
results.append(('WARN', f'Description quá ngắn ({len(desc)} ký tự) — Nên ≥{MIN_DESCRIPTION_LENGTH} ký tự'))
|
|
207
|
-
elif len(desc.split()) < MIN_DESCRIPTION_WORDS:
|
|
208
|
-
results.append(('WARN', f'Description quá ít từ ({len(desc.split())} từ) — Nên ≥{MIN_DESCRIPTION_WORDS} từ'))
|
|
209
|
-
else:
|
|
210
|
-
results.append(('PASS', f'Description đầy đủ ({len(desc)} ký tự, {len(desc.split())} từ)'))
|
|
211
|
-
|
|
212
|
-
# Check name (TÙY CHỌN)
|
|
213
|
-
name = frontmatter.get('name', '')
|
|
214
|
-
if name:
|
|
215
|
-
if ' ' in name or any(c.isupper() for c in name):
|
|
216
|
-
results.append(('WARN', f'Tên skill `{name}` nên dùng kebab-case (vd: my-skill-name)'))
|
|
217
|
-
elif len(name) > MAX_SKILL_NAME_LENGTH:
|
|
218
|
-
results.append(('WARN', f'Tên skill quá dài ({len(name)} ký tự) — Nên ≤{MAX_SKILL_NAME_LENGTH}'))
|
|
219
|
-
else:
|
|
220
|
-
results.append(('PASS', f'Tên skill: `{name}` (hợp lệ)'))
|
|
221
|
-
else:
|
|
222
|
-
results.append(('INFO', 'Không có trường `name` — AI sẽ dùng tên thư mục'))
|
|
223
|
-
|
|
224
|
-
return results, True
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
def validate_sections(sections, body):
|
|
228
|
-
"""Kiểm tra các sections bắt buộc và khuyến khích."""
|
|
229
|
-
results = []
|
|
230
|
-
|
|
231
|
-
# Check required sections
|
|
232
|
-
for section in REQUIRED_SECTIONS:
|
|
233
|
-
found = any(section in key for key in sections.keys())
|
|
234
|
-
if found:
|
|
235
|
-
results.append(('PASS', f'Có section `# {section.title()}`'))
|
|
236
|
-
else:
|
|
237
|
-
results.append(('FAIL', f'Thiếu section bắt buộc `# {section.title()}`'))
|
|
238
|
-
|
|
239
|
-
# Check recommended sections
|
|
240
|
-
for section in RECOMMENDED_SECTIONS:
|
|
241
|
-
found = any(section in key for key in sections.keys())
|
|
242
|
-
if found:
|
|
243
|
-
results.append(('PASS', f'Có section `# {section.title()}`'))
|
|
244
|
-
else:
|
|
245
|
-
results.append(('WARN', f'Thiếu section khuyến khích `# {section.title()}`'))
|
|
246
|
-
|
|
247
|
-
# Check examples count
|
|
248
|
-
example_count = count_examples(body)
|
|
249
|
-
if example_count >= 2:
|
|
250
|
-
results.append(('PASS', f'Có {example_count} ví dụ (đạt chuẩn ≥2)'))
|
|
251
|
-
elif example_count == 1:
|
|
252
|
-
results.append(('WARN', f'Chỉ có {example_count} ví dụ — Nên có ≥2 ví dụ để giảm hallucination'))
|
|
253
|
-
else:
|
|
254
|
-
results.append(('WARN', 'Không tìm thấy ví dụ nào — Rất khuyến khích thêm ≥2 ví dụ'))
|
|
255
|
-
|
|
256
|
-
# Check constraints count
|
|
257
|
-
constraint_count = count_constraints(body)
|
|
258
|
-
if constraint_count >= 1:
|
|
259
|
-
results.append(('PASS', f'Có {constraint_count} constraint(s)'))
|
|
260
|
-
else:
|
|
261
|
-
results.append(('WARN', 'Không tìm thấy constraint nào — Nên có ít nhất 1 quy tắc "KHÔNG ĐƯỢC"'))
|
|
262
|
-
|
|
263
|
-
return results
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
def validate_directory(skill_dir):
|
|
267
|
-
"""Kiểm tra cấu trúc thư mục skill."""
|
|
268
|
-
results = []
|
|
269
|
-
skill_path = Path(skill_dir)
|
|
270
|
-
|
|
271
|
-
# Check SKILL.md exists
|
|
272
|
-
skill_md = skill_path / 'SKILL.md'
|
|
273
|
-
if skill_md.exists():
|
|
274
|
-
results.append(('PASS', f'File `SKILL.md` tồn tại ({skill_md.stat().st_size} bytes)'))
|
|
275
|
-
else:
|
|
276
|
-
results.append(('FAIL', 'Không tìm thấy `SKILL.md` trong thư mục'))
|
|
277
|
-
return results, None
|
|
278
|
-
|
|
279
|
-
# Check subfolders
|
|
280
|
-
for item in skill_path.iterdir():
|
|
281
|
-
if item.is_dir():
|
|
282
|
-
folder_name = item.name
|
|
283
|
-
if folder_name in VALID_SUBFOLDERS:
|
|
284
|
-
file_count = len(list(item.rglob('*')))
|
|
285
|
-
results.append(('PASS', f'Thư mục `{folder_name}/` ({file_count} items)'))
|
|
286
|
-
elif folder_name.startswith('.'):
|
|
287
|
-
continue # Bỏ qua hidden folders
|
|
288
|
-
else:
|
|
289
|
-
results.append(('WARN', f'Thư mục `{folder_name}/` không phải chuẩn (scripts/resources/examples)'))
|
|
290
|
-
|
|
291
|
-
return results, skill_md
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
# ============================================================
|
|
295
|
-
# MAIN
|
|
296
|
-
# ============================================================
|
|
297
|
-
|
|
298
|
-
def validate_skill(target_path):
|
|
299
|
-
"""Chạy toàn bộ kiểm tra cho skill."""
|
|
300
|
-
Colors.enable_windows()
|
|
301
|
-
|
|
302
|
-
target = Path(target_path)
|
|
303
|
-
all_results = []
|
|
304
|
-
|
|
305
|
-
print(f"\n{Colors.BOLD}{'='*60}{Colors.RESET}")
|
|
306
|
-
print(f"{Colors.BOLD}🔍 SKILL VALIDATOR — Kiểm tra chất lượng AI Skill{Colors.RESET}")
|
|
307
|
-
print(f"{Colors.BOLD}{'='*60}{Colors.RESET}")
|
|
308
|
-
print(f"{Colors.INFO}📁 Target: {target.absolute()}{Colors.RESET}\n")
|
|
309
|
-
|
|
310
|
-
# ---- Phần 1: Kiểm tra thư mục (nếu là folder) ----
|
|
311
|
-
skill_md_path = None
|
|
312
|
-
|
|
313
|
-
if target.is_dir():
|
|
314
|
-
print(f"{Colors.BOLD}📂 Kiểm tra cấu trúc thư mục:{Colors.RESET}")
|
|
315
|
-
dir_results, skill_md_path = validate_directory(target)
|
|
316
|
-
all_results.extend(dir_results)
|
|
317
|
-
for status, msg in dir_results:
|
|
318
|
-
print_result(status, msg)
|
|
319
|
-
print()
|
|
320
|
-
|
|
321
|
-
if skill_md_path is None:
|
|
322
|
-
print(f"\n{Colors.FAIL}💀 KHÔNG THỂ TIẾP TỤC — Thiếu SKILL.md{Colors.RESET}\n")
|
|
323
|
-
return False
|
|
324
|
-
elif target.is_file() and target.name == 'SKILL.md':
|
|
325
|
-
skill_md_path = target
|
|
326
|
-
else:
|
|
327
|
-
print(f"{Colors.FAIL}❌ Target phải là thư mục skill hoặc file SKILL.md{Colors.RESET}")
|
|
328
|
-
return False
|
|
329
|
-
|
|
330
|
-
# ---- Phần 2: Đọc và parse SKILL.md ----
|
|
331
|
-
try:
|
|
332
|
-
content = skill_md_path.read_text(encoding='utf-8')
|
|
333
|
-
except UnicodeDecodeError:
|
|
334
|
-
content = skill_md_path.read_text(encoding='utf-8', errors='ignore')
|
|
335
|
-
|
|
336
|
-
frontmatter, body = parse_frontmatter(content)
|
|
337
|
-
sections = find_sections(body)
|
|
338
|
-
|
|
339
|
-
# ---- Phần 3: Kiểm tra frontmatter ----
|
|
340
|
-
print(f"{Colors.BOLD}📝 Kiểm tra YAML Frontmatter:{Colors.RESET}")
|
|
341
|
-
fm_results, fm_valid = validate_frontmatter(frontmatter)
|
|
342
|
-
all_results.extend(fm_results)
|
|
343
|
-
for status, msg in fm_results:
|
|
344
|
-
print_result(status, msg)
|
|
345
|
-
print()
|
|
346
|
-
|
|
347
|
-
# ---- Phần 4: Kiểm tra sections ----
|
|
348
|
-
print(f"{Colors.BOLD}📋 Kiểm tra nội dung Sections:{Colors.RESET}")
|
|
349
|
-
sec_results = validate_sections(sections, body)
|
|
350
|
-
all_results.extend(sec_results)
|
|
351
|
-
for status, msg in sec_results:
|
|
352
|
-
print_result(status, msg)
|
|
353
|
-
print()
|
|
354
|
-
|
|
355
|
-
# ---- Phần 5: Thống kê ----
|
|
356
|
-
total_lines = len(content.split('\n'))
|
|
357
|
-
total_words = len(content.split())
|
|
358
|
-
|
|
359
|
-
print(f"{Colors.BOLD}📊 Thống kê:{Colors.RESET}")
|
|
360
|
-
print_result('INFO', f'Tổng: {total_lines} dòng, {total_words} từ, {len(content)} bytes')
|
|
361
|
-
print_result('INFO', f'Sections tìm thấy: {", ".join(sections.keys()) if sections else "(không có)"}')
|
|
362
|
-
print()
|
|
363
|
-
|
|
364
|
-
# ---- Phần 6: Tổng kết ----
|
|
365
|
-
fails = sum(1 for s, _ in all_results if s == 'FAIL')
|
|
366
|
-
warns = sum(1 for s, _ in all_results if s == 'WARN')
|
|
367
|
-
passes = sum(1 for s, _ in all_results if s == 'PASS')
|
|
368
|
-
|
|
369
|
-
print(f"{Colors.BOLD}{'='*60}{Colors.RESET}")
|
|
370
|
-
print(f"{Colors.BOLD}📋 KẾT QUẢ:{Colors.RESET}")
|
|
371
|
-
print(f" {Colors.PASS}✅ PASS: {passes}{Colors.RESET}")
|
|
372
|
-
print(f" {Colors.WARN}⚠️ WARN: {warns}{Colors.RESET}")
|
|
373
|
-
print(f" {Colors.FAIL}❌ FAIL: {fails}{Colors.RESET}")
|
|
374
|
-
|
|
375
|
-
if fails == 0 and warns == 0:
|
|
376
|
-
print(f"\n {Colors.PASS}{Colors.BOLD}🎉 TUYỆT VỜI! Skill đạt chuẩn hoàn hảo!{Colors.RESET}")
|
|
377
|
-
grade = 'A+'
|
|
378
|
-
elif fails == 0 and warns <= 2:
|
|
379
|
-
print(f"\n {Colors.PASS}{Colors.BOLD}👍 TỐT! Skill hợp lệ, có thể cải thiện thêm.{Colors.RESET}")
|
|
380
|
-
grade = 'A'
|
|
381
|
-
elif fails == 0:
|
|
382
|
-
print(f"\n {Colors.WARN}{Colors.BOLD}⚠️ ĐẠT! Nhưng nên sửa các cảnh báo.{Colors.RESET}")
|
|
383
|
-
grade = 'B'
|
|
384
|
-
elif fails <= 2:
|
|
385
|
-
print(f"\n {Colors.FAIL}{Colors.BOLD}❌ CHƯA ĐẠT! Cần sửa {fails} lỗi.{Colors.RESET}")
|
|
386
|
-
grade = 'C'
|
|
387
|
-
else:
|
|
388
|
-
print(f"\n {Colors.FAIL}{Colors.BOLD}💀 KHÔNG ĐẠT! Cần sửa {fails} lỗi nghiêm trọng.{Colors.RESET}")
|
|
389
|
-
grade = 'F'
|
|
390
|
-
|
|
391
|
-
print(f"\n {Colors.BOLD}📊 Grade: {grade}{Colors.RESET}")
|
|
392
|
-
print(f"{Colors.BOLD}{'='*60}{Colors.RESET}\n")
|
|
393
|
-
|
|
394
|
-
return fails == 0
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
if __name__ == '__main__':
|
|
398
|
-
if len(sys.argv) < 2:
|
|
399
|
-
print("Sử dụng: python validate_skill.py <path-to-SKILL.md-or-skill-folder>")
|
|
400
|
-
print("Ví dụ: python validate_skill.py ./my-skill/")
|
|
401
|
-
print(" python validate_skill.py ./my-skill/SKILL.md")
|
|
402
|
-
sys.exit(1)
|
|
403
|
-
|
|
404
|
-
target = sys.argv[1]
|
|
405
|
-
|
|
406
|
-
if not os.path.exists(target):
|
|
407
|
-
print(f"❌ Không tìm thấy: {target}")
|
|
408
|
-
sys.exit(1)
|
|
409
|
-
|
|
410
|
-
success = validate_skill(target)
|
|
411
|
-
sys.exit(0 if success else 1)
|
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: tailwind-mastery
|
|
3
|
-
description: 'Master Tailwind CSS utilities, responsive design, and accessibility patterns. Use when the user mentions "Tailwind", "Tailwind CSS", "utility-first", "responsive design", "dark mode", "focus-visible", "motion-reduce", or "Tailwind v4". Covers utility patterns, layout, responsive design, components, accessibility, and performance. For React styling, see react-mastery. For design systems, see refactoring-ui.'
|
|
4
|
-
license: MIT
|
|
5
|
-
metadata:
|
|
6
|
-
author: todyle
|
|
7
|
-
version: "1.0.0"
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
# Tailwind CSS Mastery Framework
|
|
11
|
-
|
|
12
|
-
A comprehensive guide to building production UIs with Tailwind CSS utilities, responsive patterns, accessibility, and performance optimization. Apply these principles when styling web applications, building component libraries, implementing dark mode, or optimizing CSS output.
|
|
13
|
-
|
|
14
|
-
## Core Principle
|
|
15
|
-
|
|
16
|
-
**Utility-first CSS means composing designs directly in HTML with small, single-purpose classes.** Instead of writing custom CSS for every component, you combine utilities (`flex`, `items-center`, `p-4`, `text-lg`) to build designs. This eliminates naming fatigue, CSS specificity wars, and dead CSS. Every class you see describes exactly what it does.
|
|
17
|
-
|
|
18
|
-
**The foundation:** Tailwind works because most CSS is repeated — the same spacing, colors, and typography patterns appear throughout an application. Utilities express these patterns directly. The build tool strips unused classes, resulting in tiny production CSS. The key is working within Tailwind's design system (spacing scale, color palette, breakpoints) rather than escaping it with arbitrary values.
|
|
19
|
-
|
|
20
|
-
## Scoring
|
|
21
|
-
|
|
22
|
-
**Goal: 10/10.** When reviewing Tailwind code, rate 0-10:
|
|
23
|
-
|
|
24
|
-
- **9-10:** Mobile-first responsive, dark mode, focus-visible, motion-reduce, semantic color naming, consistent spacing scale
|
|
25
|
-
- **7-8:** Good utility usage with minor issues (arbitrary values for scale values, missing dark mode, inconsistent spacing)
|
|
26
|
-
- **5-6:** Working styles but overusing @apply, arbitrary values abundant, no responsive design
|
|
27
|
-
- **3-4:** Tailwind as CSS-in-HTML (every value arbitrary), no dark mode, no accessibility
|
|
28
|
-
- **1-2:** Fighting Tailwind with custom CSS, important! everywhere, no design system
|
|
29
|
-
|
|
30
|
-
## The Tailwind Mastery Framework
|
|
31
|
-
|
|
32
|
-
Six disciplines for production Tailwind CSS:
|
|
33
|
-
|
|
34
|
-
### 1. Utility Patterns & Design System
|
|
35
|
-
|
|
36
|
-
**Core concept:** Use Tailwind's design tokens (spacing scale, color palette, font sizes) instead of arbitrary values. The spacing scale (4px increments: `p-1`=4px, `p-2`=8px, `p-4`=16px) creates visual consistency. Extend the design system via `tailwind.config.js` for brand colors, not with arbitrary `[]` values.
|
|
37
|
-
|
|
38
|
-
**Why it works:** Constraints breed consistency. When everyone uses `gap-4` instead of `gap-[15px]`, spacing is uniform across the entire application. The predefined scale means fewer design decisions and faster development. Custom theme values (`bg-primary`, `text-success`) encode brand semantics.
|
|
39
|
-
|
|
40
|
-
**Key insights:**
|
|
41
|
-
- Spacing scale: `p-1`=4px, `p-2`=8px, `p-4`=16px, `p-6`=24px, `p-8`=32px
|
|
42
|
-
- Use scale values over arbitrary: `gap-6` not `gap-[23px]`
|
|
43
|
-
- Color opacity: `bg-black/50` (50% opacity) instead of separate `opacity-50`
|
|
44
|
-
- `space-y-4` for vertical lists — adds margin between children
|
|
45
|
-
- `size-6` shorthand for equal width+height (Tailwind v4)
|
|
46
|
-
- `shrink-0` shorthand (v4) instead of `flex-shrink-0`
|
|
47
|
-
- Extend theme for brand colors: `bg-primary`, `text-cta`, `border-success`
|
|
48
|
-
- `bg-linear-to-r` (v4) replaces `bg-gradient-to-r`
|
|
49
|
-
|
|
50
|
-
**Code applications:**
|
|
51
|
-
|
|
52
|
-
| Context | Pattern | Example |
|
|
53
|
-
|---------|---------|---------|
|
|
54
|
-
| **Spacing** | Scale values | `p-4 m-6 gap-8` |
|
|
55
|
-
| **Color opacity** | Slash syntax | `bg-black/50 text-white/80` |
|
|
56
|
-
| **List spacing** | space-y | `<div class="space-y-4">` |
|
|
57
|
-
| **Brand color** | Theme extend | `bg-primary text-on-primary` |
|
|
58
|
-
| **Square element** | size utility | `size-8` (= `h-8 w-8`) |
|
|
59
|
-
| **Gradient** | bg-linear (v4) | `bg-linear-to-r from-blue-500 to-purple-500` |
|
|
60
|
-
|
|
61
|
-
### 2. Layout
|
|
62
|
-
|
|
63
|
-
**Core concept:** Container with `max-w-7xl mx-auto` for content width. `flex` for one-dimensional, `grid` for two-dimensional layouts. Responsive padding `px-4 md:px-6 lg:px-8`. Container queries `@container` for component-level responsiveness.
|
|
64
|
-
|
|
65
|
-
**Why it works:** `max-w-7xl mx-auto` prevents content from stretching to unreadable widths on large screens. `gap-*` replaces manual margins on grid/flex children, simplifying layout code. Container queries enable components to respond to their container size rather than the viewport — essential for reusable components.
|
|
66
|
-
|
|
67
|
-
**Key insights:**
|
|
68
|
-
- `max-w-7xl mx-auto px-4` for main content container
|
|
69
|
-
- `flex items-center justify-between` — most common header pattern
|
|
70
|
-
- `grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6` for responsive grid
|
|
71
|
-
- `gap-*` on flex/grid containers, not margins on children
|
|
72
|
-
- Responsive padding: `px-4 sm:px-6 lg:px-8`
|
|
73
|
-
- `@container` + `@lg:grid-cols-2` for component-level responsive (v3.2+)
|
|
74
|
-
- Negative margins sparingly: `-mt-8` for intentional overlapping effects
|
|
75
|
-
|
|
76
|
-
**Code applications:**
|
|
77
|
-
|
|
78
|
-
| Context | Pattern | Example |
|
|
79
|
-
|---------|---------|---------|
|
|
80
|
-
| **Container** | max-w + padding | `<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">` |
|
|
81
|
-
| **Flex row** | items-center | `<nav class="flex items-center justify-between h-16">` |
|
|
82
|
-
| **Grid** | Responsive cols | `<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">` |
|
|
83
|
-
| **Container query** | @container | `<div class="@container"><div class="@lg:grid-cols-2">` |
|
|
84
|
-
|
|
85
|
-
### 3. Responsive Design
|
|
86
|
-
|
|
87
|
-
**Core concept:** Mobile-first — write base styles for mobile, add breakpoints for larger screens via `sm:`, `md:`, `lg:`, `xl:`, `2xl:` prefixes. Test at all breakpoints: 320, 375, 768, 1024, 1280, 1536px. Use `hidden md:block` for visibility control.
|
|
88
|
-
|
|
89
|
-
**Why it works:** Mobile-first ensures the smallest, most constrained screens get attention first. Adding complexity for larger screens is natural — hiding sidebar on mobile, showing it on desktop. This approach also results in smaller CSS because mobile styles don't need breakpoint prefixes.
|
|
90
|
-
|
|
91
|
-
**Key insights:**
|
|
92
|
-
- Base styles = mobile. Breakpoints add desktop enhancements
|
|
93
|
-
- `text-sm md:text-base lg:text-lg` — progressive text sizing
|
|
94
|
-
- `hidden md:flex` — hide on mobile, show on desktop
|
|
95
|
-
- `flex-col md:flex-row` — stack on mobile, row on desktop
|
|
96
|
-
- Test at 320px (smallest iPhone SE) — don't ignore small screens
|
|
97
|
-
- `aspect-video` with `object-cover` for responsive media
|
|
98
|
-
- `srcset` with `sizes` attribute for responsive images (HTML, not Tailwind)
|
|
99
|
-
|
|
100
|
-
**Code applications:**
|
|
101
|
-
|
|
102
|
-
| Context | Pattern | Example |
|
|
103
|
-
|---------|---------|---------|
|
|
104
|
-
| **Mobile-first** | Base + breakpoint | `<h1 class="text-2xl md:text-4xl lg:text-5xl">` |
|
|
105
|
-
| **Show/hide** | hidden + display | `<nav class="hidden md:flex">` (mobile menu alternative) |
|
|
106
|
-
| **Stack → row** | Direction change | `<div class="flex flex-col md:flex-row gap-4">` |
|
|
107
|
-
| **Responsive image** | aspect + object | `<img class="aspect-video object-cover w-full rounded-xl">` |
|
|
108
|
-
| **Responsive padding** | Breakpoint padding | `<main class="px-4 sm:px-6 lg:px-8">` |
|
|
109
|
-
|
|
110
|
-
### 4. Components
|
|
111
|
-
|
|
112
|
-
**Core concept:** Buttons need consistent sizing (`px-4 py-2`), minimum touch targets on mobile (`min-h-[44px]`), loading states, and proper icon button labels. Cards use `rounded-lg shadow-md` with hover feedback. Forms need visible focus indicators (`focus:ring-2`), disabled states, and keyboard accessibility.
|
|
113
|
-
|
|
114
|
-
**Why it works:** Consistency in component sizing creates visual rhythm. Touch targets of 44px minimum (Apple HIG standard) prevent frustrating tap misses on mobile. Focus indicators are essential for keyboard navigation and WCAG compliance. Interactive elements must provide feedback — hover, focus, active, disabled states.
|
|
115
|
-
|
|
116
|
-
**Key insights:**
|
|
117
|
-
- Buttons: `px-4 py-2 rounded-lg font-medium transition-colors`
|
|
118
|
-
- Touch targets: `min-h-[44px] min-w-[44px]` on all interactive mobile elements
|
|
119
|
-
- Loading button: `disabled + opacity-50 + spinner icon`
|
|
120
|
-
- Cards: `rounded-2xl shadow-lg p-6 hover:shadow-xl transition-shadow`
|
|
121
|
-
- Inputs: `h-10 w-full px-3 rounded-md border focus:ring-2 focus:ring-blue-500`
|
|
122
|
-
- Disabled: `disabled:opacity-50 disabled:cursor-not-allowed`
|
|
123
|
-
- Icon buttons: always include `aria-label`
|
|
124
|
-
|
|
125
|
-
**Code applications:**
|
|
126
|
-
|
|
127
|
-
| Context | Pattern | Example |
|
|
128
|
-
|---------|---------|---------|
|
|
129
|
-
| **Button** | Standard | `<button class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors">` |
|
|
130
|
-
| **Touch target** | Mobile minimum | `<button class="min-h-[44px] min-w-[44px]">` |
|
|
131
|
-
| **Card** | Hover feedback | `<div class="rounded-2xl shadow-lg p-6 hover:shadow-xl transition-shadow">` |
|
|
132
|
-
| **Input** | Focus ring | `<input class="h-10 px-3 rounded-md border focus:ring-2 focus:ring-blue-500">` |
|
|
133
|
-
| **Disabled** | Opacity + cursor | `<button disabled class="disabled:opacity-50 disabled:cursor-not-allowed">` |
|
|
134
|
-
| **Icon button** | aria-label | `<button aria-label="Close"><XIcon class="size-5"/></button>` |
|
|
135
|
-
|
|
136
|
-
### 5. Accessibility
|
|
137
|
-
|
|
138
|
-
**Core concept:** `sr-only` for screen reader text, `focus-visible:ring-2` for keyboard-only focus indicators, `motion-reduce:animate-none` for motion sensitivity, semantic HTML always. Accessibility is not optional — it's a legal requirement in many jurisdictions.
|
|
139
|
-
|
|
140
|
-
**Why it works:** `focus-visible` shows focus rings only for keyboard users (not mouse clicks), improving both aesthetics and accessibility. `motion-reduce` respects OS-level reduced motion preferences — essential for users with vestibular disorders. `sr-only` provides context to screen readers without affecting visual design.
|
|
141
|
-
|
|
142
|
-
**Key insights:**
|
|
143
|
-
- `sr-only` for text visible only to screen readers
|
|
144
|
-
- `focus-visible:ring-2` instead of `focus:ring-2` — keyboard-only focus
|
|
145
|
-
- `motion-reduce:animate-none` respects prefers-reduced-motion
|
|
146
|
-
- `motion-reduce:transition-none` for transition-heavy components
|
|
147
|
-
- Never `outline-none` without a replacement focus indicator
|
|
148
|
-
- Always `aria-label` on icon-only buttons
|
|
149
|
-
- Dark mode: `dark:bg-gray-900 dark:text-gray-100` — ensure sufficient contrast
|
|
150
|
-
- SVG explicit dimensions: `<svg class="size-6" width="24" height="24">` to prevent layout shift
|
|
151
|
-
|
|
152
|
-
**Code applications:**
|
|
153
|
-
|
|
154
|
-
| Context | Pattern | Example |
|
|
155
|
-
|---------|---------|---------|
|
|
156
|
-
| **Screen reader** | sr-only | `<span class="sr-only">Close menu</span>` |
|
|
157
|
-
| **Keyboard focus** | focus-visible | `<button class="focus-visible:ring-2 focus-visible:ring-blue-500">` |
|
|
158
|
-
| **Reduced motion** | motion-reduce | `<div class="animate-pulse motion-reduce:animate-none">` |
|
|
159
|
-
| **Dark mode** | dark: prefix | `<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">` |
|
|
160
|
-
| **Icon a11y** | aria-label | `<button aria-label="Delete item"><TrashIcon class="size-5"/></button>` |
|
|
161
|
-
| **SVG dims** | Explicit w/h | `<svg class="size-6" width="24" height="24">` |
|
|
162
|
-
|
|
163
|
-
### 6. Performance & Configuration
|
|
164
|
-
|
|
165
|
-
**Core concept:** Configure `content` paths correctly so Tailwind can tree-shake unused classes. Avoid overusing `@apply` — it defeats the purpose of utility-first CSS. Use JIT mode (default in v3+) for development performance. Custom utilities in theme config for repeated one-off values.
|
|
166
|
-
|
|
167
|
-
**Why it works:** Tailwind's production CSS is tiny because it only includes classes actually used in your source files. Incorrect `content` configuration results in either missing styles (too narrow) or bloated CSS (too broad). `@apply` creates abstraction layers that Tailwind's utility-first approach was designed to eliminate.
|
|
168
|
-
|
|
169
|
-
**Key insights:**
|
|
170
|
-
- `content: ['./src/**/*.{js,ts,jsx,tsx}']` — must cover all template files
|
|
171
|
-
- `@apply` sparingly — prefer direct utilities in HTML/JSX
|
|
172
|
-
- Custom utilities in `tailwind.config.js` for repeated arbitrary values
|
|
173
|
-
- `@tailwindcss/forms` for consistent form element reset
|
|
174
|
-
- `@tailwindcss/typography` (`prose`) for markdown/CMS content
|
|
175
|
-
- Group and Peer for parent/sibling state: `group-hover:text-blue-500`
|
|
176
|
-
- `peer-checked:bg-blue-100` for checkbox/radio state styling
|
|
177
|
-
|
|
178
|
-
**Code applications:**
|
|
179
|
-
|
|
180
|
-
| Context | Pattern | Example |
|
|
181
|
-
|---------|---------|---------|
|
|
182
|
-
| **Content config** | Glob patterns | `content: ['./src/**/*.{js,ts,jsx,tsx,html}']` |
|
|
183
|
-
| **Custom utility** | Theme extend | `extend: { boxShadow: { card: '0 4px 20px rgba(0,0,0,.08)' } }` |
|
|
184
|
-
| **Plugin** | Official | `plugins: [require('@tailwindcss/forms'), require('@tailwindcss/typography')]` |
|
|
185
|
-
| **Group hover** | Parent state | `<div class="group"><span class="group-hover:text-blue-500">` |
|
|
186
|
-
| **Peer** | Sibling state | `<input class="peer"><label class="peer-checked:font-bold">` |
|
|
187
|
-
| **Prose** | Typography | `<article class="prose prose-lg max-w-none dark:prose-invert">` |
|
|
188
|
-
|
|
189
|
-
## Common Mistakes
|
|
190
|
-
|
|
191
|
-
| Mistake | Why It Fails | Fix |
|
|
192
|
-
|---------|-------------|-----|
|
|
193
|
-
| **Arbitrary values for scale values** | Inconsistent spacing/sizing | Use `p-4` not `p-[15px]` |
|
|
194
|
-
| **Heavy @apply usage** | Creates CSS abstractions that defeat utility-first | Use utilities directly in HTML |
|
|
195
|
-
| **No dark mode** | Poor UX in low-light, not respecting OS preference | Add `dark:` variants |
|
|
196
|
-
| **outline-none without replacement** | Keyboard users can't see focus | `focus-visible:ring-2` |
|
|
197
|
-
| **No motion-reduce** | Harms vestibular disorder users | `motion-reduce:animate-none` |
|
|
198
|
-
| **Small touch targets** | Frustrating mobile experience | `min-h-[44px]` minimum |
|
|
199
|
-
| **Missing content paths** | Styles missing in production | Verify all template file paths |
|
|
200
|
-
| **Desktop-first approach** | Complex mobile overrides | Start with mobile, add `md:` `lg:` |
|
|
201
|
-
| **bg-gradient-to in v4** | Deprecated syntax | Use `bg-linear-to-r` in Tailwind v4 |
|
|
202
|
-
| **SVGs without explicit dimensions** | Layout shift before CSS loads | Add `width` and `height` attributes |
|
|
203
|
-
|
|
204
|
-
## Quick Diagnostic
|
|
205
|
-
|
|
206
|
-
| Question | If No | Action |
|
|
207
|
-
|----------|-------|--------|
|
|
208
|
-
| Using Tailwind's spacing scale? | Inconsistent spacing | Replace arbitrary values with scale tokens |
|
|
209
|
-
| Mobile-first responsive? | Complex overrides | Start with mobile base, add breakpoints |
|
|
210
|
-
| Dark mode implemented? | Missing OS preference respect | Add `dark:` variants for all colors |
|
|
211
|
-
| Focus indicators visible? | Keyboard a11y broken | Add `focus-visible:ring-2` |
|
|
212
|
-
| Motion preferences respected? | A11y violation | Add `motion-reduce:` variants |
|
|
213
|
-
| Touch targets ≥44px? | Mobile tap frustration | Add `min-h-[44px]` |
|
|
214
|
-
| Content paths correct? | Missing production styles | Audit tailwind.config.js content |
|
|
215
|
-
| Using semantic colors? | Hard to rebrand | Move to `bg-primary` theme tokens |
|
|
216
|
-
| Icon buttons labeled? | Screen reader fails | Add `aria-label` to all icon buttons |
|
|
217
|
-
| SVGs have explicit dimensions? | Layout shift | Add `width` and `height` attributes |
|
|
218
|
-
|
|
219
|
-
## Further Reading
|
|
220
|
-
|
|
221
|
-
- [Tailwind CSS Documentation](https://tailwindcss.com/docs) — Official reference
|
|
222
|
-
- [Tailwind CSS v4 Migration](https://tailwindcss.com/docs/upgrade-guide) — v3 to v4 changes
|
|
223
|
-
- [Refactoring UI](https://www.refactoringui.com/) — Design principles from Tailwind creators
|
|
224
|
-
- [@tailwindcss/typography](https://tailwindcss.com/docs/typography-plugin) — Prose styling
|
|
225
|
-
- [Heroicons](https://heroicons.com/) — SVG icons from Tailwind Labs
|
|
226
|
-
|
|
227
|
-
## About
|
|
228
|
-
|
|
229
|
-
This skill synthesizes patterns from Adam Wathan (Tailwind creator), Steve Schoger (Refactoring UI), and the Tailwind CSS community. For visual design principles, see refactoring-ui. For top-tier web design, see top-design. For React integration, see react-mastery.
|