@kood/claude-code 0.7.6 → 0.7.8
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/dist/index.js +215 -39
- package/package.json +1 -1
- package/templates/.claude/skills/design/SKILL.md +199 -0
- package/templates/.claude/skills/design/references/accessibility.md +106 -0
- package/templates/.claude/skills/design/references/checklist.md +41 -0
- package/templates/.claude/skills/design/references/core-principles.md +66 -0
- package/templates/.claude/skills/design/references/global-uiux.md +216 -0
- package/templates/.claude/skills/design/references/korea-uiux.md +227 -0
- package/templates/.claude/skills/design/references/patterns.md +250 -0
- package/templates/.claude/skills/design/references/responsive.md +248 -0
- package/templates/.claude/skills/design/references/safe-area.md +202 -0
- package/templates/.claude/skills/design/references/systems.md +116 -0
- package/templates/.claude/skills/design/references/trends.md +75 -0
- package/templates/.claude/skills/global-uiux-design/AGENTS.md +0 -320
- package/templates/.claude/skills/global-uiux-design/SKILL.md +0 -1089
- package/templates/.claude/skills/global-uiux-design/references/accessibility.md +0 -401
- package/templates/.claude/skills/global-uiux-design/references/color-system.md +0 -275
- package/templates/.claude/skills/global-uiux-design/references/design-philosophy.md +0 -206
- package/templates/.claude/skills/global-uiux-design/references/design-systems.md +0 -446
- package/templates/.claude/skills/korea-uiux-design/AGENTS.md +0 -310
- package/templates/.claude/skills/korea-uiux-design/SKILL.md +0 -980
- package/templates/.claude/skills/korea-uiux-design/references/accessibility.md +0 -298
- package/templates/.claude/skills/korea-uiux-design/references/checklist.md +0 -107
- package/templates/.claude/skills/korea-uiux-design/references/color-system.md +0 -156
- package/templates/.claude/skills/korea-uiux-design/references/design-philosophy.md +0 -25
- package/templates/.claude/skills/korea-uiux-design/references/icon-guide.md +0 -180
- package/templates/.claude/skills/korea-uiux-design/references/micro-interactions.md +0 -259
- package/templates/.claude/skills/korea-uiux-design/references/responsive-patterns.md +0 -115
- package/templates/.claude/skills/korea-uiux-design/references/service-patterns.md +0 -206
- package/templates/.claude/skills/korea-uiux-design/references/state-patterns.md +0 -320
- package/templates/.claude/skills/korea-uiux-design/references/typography.md +0 -70
package/dist/index.js
CHANGED
|
@@ -22,8 +22,8 @@ var banner = () => {
|
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
// src/commands/init.ts
|
|
25
|
-
import
|
|
26
|
-
import
|
|
25
|
+
import fs9 from "fs-extra";
|
|
26
|
+
import os2 from "os";
|
|
27
27
|
|
|
28
28
|
// src/features/templates/template-path-resolver.ts
|
|
29
29
|
import path2 from "path";
|
|
@@ -688,16 +688,50 @@ async function promptScopeSelection(options) {
|
|
|
688
688
|
}
|
|
689
689
|
return { scope: response.value };
|
|
690
690
|
}
|
|
691
|
+
async function promptCodexSync(options) {
|
|
692
|
+
const { providedSyncCodex } = options;
|
|
693
|
+
if (providedSyncCodex !== void 0) {
|
|
694
|
+
return { syncCodex: providedSyncCodex };
|
|
695
|
+
}
|
|
696
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
697
|
+
return { syncCodex: false };
|
|
698
|
+
}
|
|
699
|
+
logger.blank();
|
|
700
|
+
const result = await promptConfirm(
|
|
701
|
+
"Also sync with Codex now? (~/.codex/skills/)",
|
|
702
|
+
false
|
|
703
|
+
);
|
|
704
|
+
return { syncCodex: result.confirmed };
|
|
705
|
+
}
|
|
691
706
|
|
|
692
707
|
// src/shared/gitignore-manager.ts
|
|
693
708
|
import fs7 from "fs-extra";
|
|
694
709
|
import path9 from "path";
|
|
695
710
|
var CLAUDE_GENERATED_FOLDERS = [
|
|
711
|
+
".claude/brainstorms/",
|
|
712
|
+
".claude/crawler/",
|
|
713
|
+
".claude/first-principles/",
|
|
714
|
+
".claude/genius-ideas/",
|
|
696
715
|
".claude/plan/",
|
|
716
|
+
".claude/plans/",
|
|
697
717
|
".claude/ralph/",
|
|
698
718
|
".claude/refactor/",
|
|
699
|
-
".claude/prd/"
|
|
719
|
+
".claude/prd/",
|
|
720
|
+
".claude/research/",
|
|
721
|
+
".claude/validation-results/"
|
|
700
722
|
];
|
|
723
|
+
function normalizeIgnorePattern(pattern) {
|
|
724
|
+
return pattern.replace(/\\/g, "/").replace(/^\.?\//, "").replace(/\/+$/, "").trim();
|
|
725
|
+
}
|
|
726
|
+
function parseIgnoreLine(line) {
|
|
727
|
+
const trimmed = line.trim();
|
|
728
|
+
if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("!")) {
|
|
729
|
+
return null;
|
|
730
|
+
}
|
|
731
|
+
const [rawPattern] = trimmed.split(/\s+#/u, 1);
|
|
732
|
+
const normalized = normalizeIgnorePattern(rawPattern);
|
|
733
|
+
return normalized || null;
|
|
734
|
+
}
|
|
701
735
|
async function updateGitignore(targetDir) {
|
|
702
736
|
const gitignorePath = path9.join(targetDir, ".gitignore");
|
|
703
737
|
const sectionComment = "# Claude Code generated files";
|
|
@@ -709,43 +743,33 @@ async function updateGitignore(targetDir) {
|
|
|
709
743
|
} catch {
|
|
710
744
|
content = "";
|
|
711
745
|
}
|
|
712
|
-
const
|
|
713
|
-
const
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
746
|
+
const eol = content.includes("\r\n") ? "\r\n" : "\n";
|
|
747
|
+
const lines = content ? content.split(/\r?\n/u) : [];
|
|
748
|
+
const existingPatterns = new Set(
|
|
749
|
+
lines.map((line) => parseIgnoreLine(line)).filter((pattern) => Boolean(pattern))
|
|
750
|
+
);
|
|
751
|
+
const linesToAdd = CLAUDE_GENERATED_FOLDERS.filter(
|
|
752
|
+
(folder) => !existingPatterns.has(normalizeIgnorePattern(folder))
|
|
753
|
+
);
|
|
719
754
|
if (linesToAdd.length === 0) {
|
|
720
755
|
logger.info(".gitignore already contains all Claude Code patterns");
|
|
721
756
|
return;
|
|
722
757
|
}
|
|
723
|
-
const
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
}
|
|
758
|
+
const sectionIndex = lines.findIndex(
|
|
759
|
+
(line) => line.trim() === sectionComment
|
|
760
|
+
);
|
|
761
|
+
const needsSection = sectionIndex === -1;
|
|
728
762
|
if (needsSection) {
|
|
729
|
-
if (
|
|
730
|
-
|
|
763
|
+
if (lines.length > 0 && lines[lines.length - 1] !== "") {
|
|
764
|
+
lines.push("");
|
|
731
765
|
}
|
|
732
|
-
|
|
733
|
-
newContent += linesToAdd.join("\n") + "\n";
|
|
766
|
+
lines.push(sectionComment, ...linesToAdd);
|
|
734
767
|
} else {
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
lines.splice(sectionIndex + 1, 0, ...linesToAdd);
|
|
741
|
-
newContent = lines.join("\n");
|
|
742
|
-
} else {
|
|
743
|
-
if (newContent) {
|
|
744
|
-
newContent += "\n";
|
|
745
|
-
}
|
|
746
|
-
newContent += sectionComment + "\n";
|
|
747
|
-
newContent += linesToAdd.join("\n") + "\n";
|
|
748
|
-
}
|
|
768
|
+
lines.splice(sectionIndex + 1, 0, ...linesToAdd);
|
|
769
|
+
}
|
|
770
|
+
let newContent = lines.join(eol);
|
|
771
|
+
if (!newContent.endsWith(eol)) {
|
|
772
|
+
newContent += eol;
|
|
749
773
|
}
|
|
750
774
|
await fs7.writeFile(gitignorePath, newContent, "utf-8");
|
|
751
775
|
if (hasGitignore) {
|
|
@@ -755,6 +779,126 @@ async function updateGitignore(targetDir) {
|
|
|
755
779
|
}
|
|
756
780
|
}
|
|
757
781
|
|
|
782
|
+
// src/features/codex-sync/codex-sync.ts
|
|
783
|
+
import fs8 from "fs-extra";
|
|
784
|
+
import os from "os";
|
|
785
|
+
import path10 from "path";
|
|
786
|
+
function resolveCodexHome() {
|
|
787
|
+
const envCodexHome = process.env.CODEX_HOME?.trim();
|
|
788
|
+
if (envCodexHome) {
|
|
789
|
+
return path10.resolve(envCodexHome);
|
|
790
|
+
}
|
|
791
|
+
return path10.join(os.homedir(), ".codex");
|
|
792
|
+
}
|
|
793
|
+
function normalizeLineEndings(content) {
|
|
794
|
+
return content.replace(/\r\n/g, "\n");
|
|
795
|
+
}
|
|
796
|
+
function yamlQuote(value) {
|
|
797
|
+
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
798
|
+
}
|
|
799
|
+
function extractDescriptionFromFrontmatter(markdown) {
|
|
800
|
+
const frontmatterMatch = markdown.match(/^---\n([\s\S]*?)\n---\n?/);
|
|
801
|
+
if (!frontmatterMatch) {
|
|
802
|
+
return void 0;
|
|
803
|
+
}
|
|
804
|
+
const lines = frontmatterMatch[1].split("\n");
|
|
805
|
+
for (const rawLine of lines) {
|
|
806
|
+
const line = rawLine.trim();
|
|
807
|
+
if (!line.startsWith("description:")) {
|
|
808
|
+
continue;
|
|
809
|
+
}
|
|
810
|
+
const value = line.slice("description:".length).trim();
|
|
811
|
+
if (!value) {
|
|
812
|
+
return void 0;
|
|
813
|
+
}
|
|
814
|
+
return value.replace(/^['"]|['"]$/g, "");
|
|
815
|
+
}
|
|
816
|
+
return void 0;
|
|
817
|
+
}
|
|
818
|
+
function buildCommandSkillContent(commandPath, commandRaw) {
|
|
819
|
+
const commandName = path10.basename(commandPath, ".md");
|
|
820
|
+
const normalizedCommand = normalizeLineEndings(commandRaw).trim();
|
|
821
|
+
const sourcePath = path10.resolve(commandPath);
|
|
822
|
+
const description = extractDescriptionFromFrontmatter(normalizedCommand) || `${commandName} command imported from .claude/commands`;
|
|
823
|
+
return `---
|
|
824
|
+
name: ${yamlQuote(commandName)}
|
|
825
|
+
description: ${yamlQuote(description)}
|
|
826
|
+
---
|
|
827
|
+
|
|
828
|
+
Imported command source: ${sourcePath}
|
|
829
|
+
|
|
830
|
+
Use this as a command playbook. If scripts are referenced with .claude-relative paths,
|
|
831
|
+
resolve them from the current workspace first; if missing, ask for the intended repo root.
|
|
832
|
+
|
|
833
|
+
---
|
|
834
|
+
|
|
835
|
+
${normalizedCommand}
|
|
836
|
+
`;
|
|
837
|
+
}
|
|
838
|
+
async function syncSkills(claudeSkillsDir, codexSkillsDir) {
|
|
839
|
+
if (!await fs8.pathExists(claudeSkillsDir)) {
|
|
840
|
+
return 0;
|
|
841
|
+
}
|
|
842
|
+
const entries = await fs8.readdir(claudeSkillsDir, { withFileTypes: true });
|
|
843
|
+
let syncedSkills = 0;
|
|
844
|
+
for (const entry of entries) {
|
|
845
|
+
if (!entry.isDirectory()) {
|
|
846
|
+
continue;
|
|
847
|
+
}
|
|
848
|
+
if (entry.name === ".system") {
|
|
849
|
+
continue;
|
|
850
|
+
}
|
|
851
|
+
const src = path10.join(claudeSkillsDir, entry.name);
|
|
852
|
+
const dest = path10.join(codexSkillsDir, entry.name);
|
|
853
|
+
if (await fs8.pathExists(dest)) {
|
|
854
|
+
await fs8.remove(dest);
|
|
855
|
+
}
|
|
856
|
+
await copyRecursive(src, dest, { files: 0, directories: 0 });
|
|
857
|
+
syncedSkills++;
|
|
858
|
+
}
|
|
859
|
+
return syncedSkills;
|
|
860
|
+
}
|
|
861
|
+
async function syncCommands(claudeCommandsDir, codexCommandsDir) {
|
|
862
|
+
if (!await fs8.pathExists(claudeCommandsDir)) {
|
|
863
|
+
return 0;
|
|
864
|
+
}
|
|
865
|
+
const entries = await fs8.readdir(claudeCommandsDir, { withFileTypes: true });
|
|
866
|
+
const commandFiles = entries.filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => entry.name).sort((a, b) => a.localeCompare(b));
|
|
867
|
+
if (commandFiles.length === 0) {
|
|
868
|
+
return 0;
|
|
869
|
+
}
|
|
870
|
+
await fs8.remove(codexCommandsDir);
|
|
871
|
+
await fs8.ensureDir(codexCommandsDir);
|
|
872
|
+
for (const commandFile of commandFiles) {
|
|
873
|
+
const commandPath = path10.join(claudeCommandsDir, commandFile);
|
|
874
|
+
const commandRaw = await fs8.readFile(commandPath, "utf8");
|
|
875
|
+
const commandName = path10.basename(commandFile, ".md");
|
|
876
|
+
const skillDir = path10.join(codexCommandsDir, commandName);
|
|
877
|
+
const skillPath = path10.join(skillDir, "SKILL.md");
|
|
878
|
+
await fs8.ensureDir(skillDir);
|
|
879
|
+
await fs8.writeFile(skillPath, buildCommandSkillContent(commandPath, commandRaw));
|
|
880
|
+
}
|
|
881
|
+
return commandFiles.length;
|
|
882
|
+
}
|
|
883
|
+
async function syncWithCodex(targetDir) {
|
|
884
|
+
const codexHome = resolveCodexHome();
|
|
885
|
+
const codexSkillsDir = path10.join(codexHome, "skills");
|
|
886
|
+
const codexCommandsDir = path10.join(codexSkillsDir, "claude-commands");
|
|
887
|
+
await fs8.ensureDir(codexSkillsDir);
|
|
888
|
+
const claudeRootDir = path10.join(targetDir, ".claude");
|
|
889
|
+
const claudeSkillsDir = path10.join(claudeRootDir, "skills");
|
|
890
|
+
const claudeCommandsDir = path10.join(claudeRootDir, "commands");
|
|
891
|
+
const syncedSkills = await syncSkills(claudeSkillsDir, codexSkillsDir);
|
|
892
|
+
const syncedCommands = await syncCommands(claudeCommandsDir, codexCommandsDir);
|
|
893
|
+
return {
|
|
894
|
+
codexHome,
|
|
895
|
+
codexSkillsDir,
|
|
896
|
+
codexCommandsDir,
|
|
897
|
+
syncedSkills,
|
|
898
|
+
syncedCommands
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
|
|
758
902
|
// src/commands/init.ts
|
|
759
903
|
var TEMPLATE_DESCRIPTIONS = {
|
|
760
904
|
"tanstack-start": "TanStack Start + React \uD480\uC2A4\uD0DD \uD504\uB85C\uC81D\uD2B8",
|
|
@@ -764,7 +908,7 @@ var TEMPLATE_DESCRIPTIONS = {
|
|
|
764
908
|
};
|
|
765
909
|
async function validateTargetDirectory(targetDir) {
|
|
766
910
|
try {
|
|
767
|
-
const stat = await
|
|
911
|
+
const stat = await fs9.stat(targetDir);
|
|
768
912
|
if (!stat.isDirectory()) {
|
|
769
913
|
logger.error(`Target is not a directory: ${targetDir}`);
|
|
770
914
|
process.exit(1);
|
|
@@ -778,7 +922,7 @@ async function validateTargetDirectory(targetDir) {
|
|
|
778
922
|
process.exit(1);
|
|
779
923
|
}
|
|
780
924
|
try {
|
|
781
|
-
await
|
|
925
|
+
await fs9.access(targetDir, fs9.constants.W_OK);
|
|
782
926
|
} catch {
|
|
783
927
|
logger.error(`No write permission for: ${targetDir}`);
|
|
784
928
|
process.exit(1);
|
|
@@ -905,7 +1049,7 @@ var init = async (options) => {
|
|
|
905
1049
|
const { scope } = await promptScopeSelection({
|
|
906
1050
|
providedScope: options.scope
|
|
907
1051
|
});
|
|
908
|
-
const targetDir = scope === "user" ?
|
|
1052
|
+
const targetDir = scope === "user" ? os2.homedir() : projectDir;
|
|
909
1053
|
const isUserScope = scope === "user";
|
|
910
1054
|
if (isUserScope) {
|
|
911
1055
|
logger.blank();
|
|
@@ -960,15 +1104,46 @@ var init = async (options) => {
|
|
|
960
1104
|
);
|
|
961
1105
|
}
|
|
962
1106
|
}
|
|
1107
|
+
const { syncCodex } = await promptCodexSync({
|
|
1108
|
+
providedSyncCodex: options.syncCodex
|
|
1109
|
+
});
|
|
1110
|
+
if (!syncCodex) {
|
|
1111
|
+
return;
|
|
1112
|
+
}
|
|
1113
|
+
logger.blank();
|
|
1114
|
+
logger.info("Syncing .claude skills/commands to Codex...");
|
|
1115
|
+
try {
|
|
1116
|
+
const result = await syncWithCodex(targetDir);
|
|
1117
|
+
if (result.syncedSkills > 0) {
|
|
1118
|
+
logger.step(`Skills synced: ${result.syncedSkills}`);
|
|
1119
|
+
}
|
|
1120
|
+
if (result.syncedCommands > 0) {
|
|
1121
|
+
logger.step(`Commands synced: ${result.syncedCommands}`);
|
|
1122
|
+
}
|
|
1123
|
+
if (result.syncedSkills === 0 && result.syncedCommands === 0) {
|
|
1124
|
+
logger.warn(
|
|
1125
|
+
"Nothing was synced. .claude/skills and .claude/commands were not found."
|
|
1126
|
+
);
|
|
1127
|
+
} else {
|
|
1128
|
+
logger.success(`Codex sync complete: ${result.codexSkillsDir}`);
|
|
1129
|
+
}
|
|
1130
|
+
} catch (error) {
|
|
1131
|
+
logger.warn(
|
|
1132
|
+
`Codex sync failed: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1133
|
+
);
|
|
1134
|
+
}
|
|
963
1135
|
};
|
|
964
1136
|
|
|
965
1137
|
// src/index.ts
|
|
966
1138
|
var program = new Command();
|
|
967
|
-
program.name("claude-code").description("Claude Code documentation installer for projects").version("0.7.
|
|
1139
|
+
program.name("claude-code").description("Claude Code documentation installer for projects").version("0.7.8");
|
|
968
1140
|
program.option(
|
|
969
1141
|
"-t, --template <names>",
|
|
970
1142
|
"template names (comma-separated: tanstack-start,hono)"
|
|
971
|
-
).option("-f, --force", "overwrite existing files without prompting").option("--cwd <path>", "target directory (default: current directory)").option("--list", "list available templates").option("-s, --skills", "install skills to .claude/skills/").option("-c, --commands", "install commands to .claude/commands/").option("-a, --agents", "install agents to .claude/agents/").option(
|
|
1143
|
+
).option("-f, --force", "overwrite existing files without prompting").option("--cwd <path>", "target directory (default: current directory)").option("--list", "list available templates").option("-s, --skills", "install skills to .claude/skills/").option("-c, --commands", "install commands to .claude/commands/").option("-a, --agents", "install agents to .claude/agents/").option(
|
|
1144
|
+
"--sync-codex",
|
|
1145
|
+
"sync installed .claude skills/commands to Codex (~/.codex/skills/)"
|
|
1146
|
+
).option("--scope <scope>", "installation scope (project|user)").action(async (options) => {
|
|
972
1147
|
banner();
|
|
973
1148
|
if (options.list) {
|
|
974
1149
|
const templates = await listAvailableTemplates();
|
|
@@ -984,7 +1159,8 @@ program.option(
|
|
|
984
1159
|
scope: options.scope,
|
|
985
1160
|
skills: options.skills,
|
|
986
1161
|
commands: options.commands,
|
|
987
|
-
agents: options.agents
|
|
1162
|
+
agents: options.agents,
|
|
1163
|
+
syncCodex: options.syncCodex
|
|
988
1164
|
});
|
|
989
1165
|
});
|
|
990
1166
|
program.parse();
|
package/package.json
CHANGED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: design
|
|
3
|
+
description: UI/UX 디자인 전문 스킬. 사용자 중심 디자인, 접근성, 반응형 레이아웃, 디자인 시스템 구축. Nielsen 휴리스틱 및 WCAG 2.2 기반.
|
|
4
|
+
user-invocable: true
|
|
5
|
+
ui-only: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
@../../instructions/agent-patterns/read-parallelization.md
|
|
9
|
+
@../../instructions/agent-patterns/agent-teams-usage.md
|
|
10
|
+
@../../instructions/validation/forbidden-patterns.md
|
|
11
|
+
@../../instructions/validation/required-behaviors.md
|
|
12
|
+
|
|
13
|
+
# Design Skill - UI/UX 전문 스킬
|
|
14
|
+
|
|
15
|
+
<context>
|
|
16
|
+
|
|
17
|
+
**Purpose:** 사용자 중심의 UI/UX 디자인 구현. 접근성, 반응형 디자인, 디자인 시스템 구축.
|
|
18
|
+
|
|
19
|
+
**Trigger:** UI/UX 디자인 구현, 랜딩 페이지 제작, 대시보드 개발, 모바일 앱 디자인, 폼 디자인
|
|
20
|
+
|
|
21
|
+
**Key Features:**
|
|
22
|
+
- Nielsen 10가지 휴리스틱 기반 UX
|
|
23
|
+
- WCAG 2.2 AA 접근성 준수
|
|
24
|
+
- 반응형 디자인 (Mobile-First)
|
|
25
|
+
- 디자인 심리학 적용 (인지부하 최소화, Fitts/Hick/Miller 법칙)
|
|
26
|
+
- 2024-2026 트렌드 반영 (AI 통합, 마이크로인터랙션, 다크모드)
|
|
27
|
+
|
|
28
|
+
</context>
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
<quick_reference>
|
|
33
|
+
|
|
34
|
+
## 빠른 참조
|
|
35
|
+
|
|
36
|
+
### Core UX 원칙 (Nielsen)
|
|
37
|
+
|
|
38
|
+
| 원칙 | 적용 |
|
|
39
|
+
|------|------|
|
|
40
|
+
| **시스템 상태 가시성** | Loading, Progress, 피드백 |
|
|
41
|
+
| **일관성** | 패턴 재사용, 플랫폼 규칙 준수 |
|
|
42
|
+
| **오류 예방** | 확인 다이얼로그, 입력 제약 |
|
|
43
|
+
| **인지부하 최소화** | 7±2 항목, 단순 메뉴 |
|
|
44
|
+
|
|
45
|
+
### 접근성 (WCAG 2.2 AA)
|
|
46
|
+
|
|
47
|
+
| 요구사항 | 기준 |
|
|
48
|
+
|---------|------|
|
|
49
|
+
| **색상 대비** | 4.5:1 (일반), 3:1 (큰 텍스트) |
|
|
50
|
+
| **터치 타겟** | 44x44px 최소 |
|
|
51
|
+
| **키보드** | Tab, Enter, Esc 지원 |
|
|
52
|
+
| **ARIA** | role, label, live 속성 |
|
|
53
|
+
|
|
54
|
+
### 레이아웃 패턴
|
|
55
|
+
|
|
56
|
+
| 패턴 | 용도 |
|
|
57
|
+
|------|------|
|
|
58
|
+
| **F-패턴** | 콘텐츠 중심 (블로그, 기사) |
|
|
59
|
+
| **Z-패턴** | 랜딩 페이지 (히어로 → CTA) |
|
|
60
|
+
| **12열 그리드** | 반응형 레이아웃 |
|
|
61
|
+
|
|
62
|
+
</quick_reference>
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
<parallel_agent_execution>
|
|
67
|
+
|
|
68
|
+
### ⚠️ Agent Teams 우선 원칙
|
|
69
|
+
|
|
70
|
+
> **복잡한 병렬 작업 시 Agent Teams를 기본으로 사용**
|
|
71
|
+
> - Agent Teams 가용 → TeamCreate → 팀원 spawn → 병렬 협업
|
|
72
|
+
> - Agent Teams 미가용 → Task 병렬 호출 (폴백)
|
|
73
|
+
|
|
74
|
+
**Agent Teams 모드 (기본)**:
|
|
75
|
+
```typescript
|
|
76
|
+
TeamCreate({ team_name: "design-team", description: "UI/UX 디자인 프로젝트" })
|
|
77
|
+
Task(subagent_type="designer", team_name="design-team", name="designer", ...)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**수명주기 관리:**
|
|
81
|
+
- 팀원 태스크 완료 → 즉시 `shutdown_request` 전송
|
|
82
|
+
- 종료 후 `TaskList`로 다음 태스크 확인
|
|
83
|
+
- 모든 작업 완료 → `TeamDelete`로 팀 해산
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## 병렬 에이전트 실행
|
|
88
|
+
|
|
89
|
+
### 핵심 원칙
|
|
90
|
+
|
|
91
|
+
| 원칙 | 실행 방법 | 디자인 적용 |
|
|
92
|
+
|------|----------|------------|
|
|
93
|
+
| **PARALLEL** | 단일 메시지에서 여러 Tool 동시 호출 | 여러 페이지 동시 디자인 |
|
|
94
|
+
| **DELEGATE** | 전문 에이전트에게 즉시 위임 | designer (UI), code-reviewer (접근성) |
|
|
95
|
+
| **SMART MODEL** | 복잡도별 모델 선택 | haiku (탐색), sonnet (구현), opus (디자인 시스템) |
|
|
96
|
+
|
|
97
|
+
### 패턴 1: 랜딩 페이지 섹션 병렬 디자인
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
// ✅ 5개 섹션 동시 구현
|
|
101
|
+
Task(subagent_type="designer", model="sonnet",
|
|
102
|
+
prompt="히어로 섹션: 헤드라인 + CTA + 이미지")
|
|
103
|
+
Task(subagent_type="designer", model="sonnet",
|
|
104
|
+
prompt="소셜 프루프 섹션: 로고, 추천사, 통계")
|
|
105
|
+
Task(subagent_type="designer", model="sonnet",
|
|
106
|
+
prompt="기능 섹션: 3열 그리드, 아이콘, 설명")
|
|
107
|
+
Task(subagent_type="designer", model="sonnet",
|
|
108
|
+
prompt="가격 테이블: 3개 티어, 비교 기능")
|
|
109
|
+
Task(subagent_type="designer", model="sonnet",
|
|
110
|
+
prompt="CTA 섹션: 최종 전환 유도")
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**예상 시간:** 순차 300초 → 병렬 60초 (5배 향상)
|
|
114
|
+
|
|
115
|
+
### 패턴 2: 반응형 Breakpoint 병렬
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
// ✅ Mobile/Tablet/Desktop 동시 구현
|
|
119
|
+
Task(subagent_type="designer", model="sonnet",
|
|
120
|
+
prompt="Mobile (320-767px): 세로 스택, 44px 터치 타겟")
|
|
121
|
+
Task(subagent_type="designer", model="sonnet",
|
|
122
|
+
prompt="Tablet (768-1023px): 2열 그리드")
|
|
123
|
+
Task(subagent_type="designer", model="sonnet",
|
|
124
|
+
prompt="Desktop (1024px+): 3열 그리드, 호버 효과")
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 패턴 3: 접근성 검증 병렬
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
// ✅ WCAG/ARIA/키보드 동시 검증
|
|
131
|
+
Task(subagent_type="code-reviewer", model="opus",
|
|
132
|
+
prompt="WCAG 2.2 AA: 색상 대비, 포커스, 텍스트 크기")
|
|
133
|
+
Task(subagent_type="code-reviewer", model="opus",
|
|
134
|
+
prompt="ARIA 속성: role, label, live")
|
|
135
|
+
Task(subagent_type="code-reviewer", model="opus",
|
|
136
|
+
prompt="키보드 네비게이션: Tab, Enter, Esc")
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
</parallel_agent_execution>
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
<detailed_references>
|
|
144
|
+
|
|
145
|
+
## 상세 참고 문서
|
|
146
|
+
|
|
147
|
+
각 주제별 상세 가이드는 references 폴더를 참조하세요.
|
|
148
|
+
|
|
149
|
+
| 파일 | 내용 |
|
|
150
|
+
|------|------|
|
|
151
|
+
| **core-principles.md** | Nielsen 휴리스틱, Don Norman 원칙, Gestalt 원칙, 디자인 심리학 |
|
|
152
|
+
| **accessibility.md** | POUR 원칙, 색상 대비, 키보드, ARIA, 스크린 리더 |
|
|
153
|
+
| **patterns.md** | 랜딩 페이지, 대시보드, 폼, 모바일 앱 패턴 |
|
|
154
|
+
| **systems.md** | 타이포그래피, 컬러, 그리드, 반응형 디자인 |
|
|
155
|
+
| **responsive.md** | Container Queries, Fluid CSS, Modern CSS, 반응형 이미지 |
|
|
156
|
+
| **korea-uiux.md** | 한국형 UI/UX: 토스/카카오/배민/당근 디자인 시스템, 타이포(Pretendard), 결제 UX |
|
|
157
|
+
| **global-uiux.md** | 글로벌 UI/UX: Material 3, Apple HIG (Liquid Glass), Fluent 2, Carbon, SLDS |
|
|
158
|
+
| **safe-area.md** | Safe Area: iOS/Android Insets, Dynamic Island, Foldable, React Native, CSS env() |
|
|
159
|
+
| **checklist.md** | DO/DON'T 항목 |
|
|
160
|
+
| **trends.md** | 2024-2026 디자인 트렌드 |
|
|
161
|
+
|
|
162
|
+
**사용 방법:**
|
|
163
|
+
```bash
|
|
164
|
+
# 디자인 원칙 확인
|
|
165
|
+
Read references/core-principles.md
|
|
166
|
+
|
|
167
|
+
# 접근성 가이드 확인
|
|
168
|
+
Read references/accessibility.md
|
|
169
|
+
|
|
170
|
+
# 랜딩 페이지 패턴 확인
|
|
171
|
+
Read references/patterns.md
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
</detailed_references>
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
<references>
|
|
179
|
+
|
|
180
|
+
## 외부 참고 자료
|
|
181
|
+
|
|
182
|
+
| 분류 | 자료 |
|
|
183
|
+
|------|------|
|
|
184
|
+
| **UX 원칙** | Nielsen Norman Group, Don Norman "The Design of Everyday Things" |
|
|
185
|
+
| **접근성** | WCAG 2.2, W3C WAI, WebAIM |
|
|
186
|
+
| **글로벌 디자인 시스템** | Material Design 3 (Expressive), Apple HIG (Liquid Glass), Fluent 2, Carbon, SLDS |
|
|
187
|
+
| **한국 디자인 시스템** | 토스 TDS, 카카오 Krane, 쿠팡 RDS, 배민, 당근 |
|
|
188
|
+
| **심리학** | Laws of UX, Gestalt 원칙, 인지부하 이론 |
|
|
189
|
+
| **트렌드** | Awwwards, Dribbble, Behance, Smashing Magazine |
|
|
190
|
+
|
|
191
|
+
### 외부 링크
|
|
192
|
+
|
|
193
|
+
- [Nielsen 10 Usability Heuristics](https://www.nngroup.com/articles/ten-usability-heuristics/)
|
|
194
|
+
- [WCAG 2.2 Guidelines](https://www.w3.org/WAI/standards-guidelines/wcag/)
|
|
195
|
+
- [Material Design 3](https://m3.material.io/)
|
|
196
|
+
- [Apple Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines/)
|
|
197
|
+
- [Laws of UX](https://lawsofux.com/)
|
|
198
|
+
|
|
199
|
+
</references>
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Accessibility (WCAG 2.2 AA)
|
|
2
|
+
|
|
3
|
+
<pour_principles>
|
|
4
|
+
|
|
5
|
+
## POUR 원칙
|
|
6
|
+
|
|
7
|
+
| 원칙 | 설명 | 구현 |
|
|
8
|
+
|------|------|------|
|
|
9
|
+
| **Perceivable** | 인지 가능 | 대체 텍스트, 자막, 색상 대비 |
|
|
10
|
+
| **Operable** | 조작 가능 | 키보드, 충분한 시간 |
|
|
11
|
+
| **Understandable** | 이해 가능 | 명확한 언어, 일관된 UI |
|
|
12
|
+
| **Robust** | 견고성 | 시맨틱 HTML, ARIA |
|
|
13
|
+
|
|
14
|
+
</pour_principles>
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
<color_contrast>
|
|
19
|
+
|
|
20
|
+
## 색상 대비 (WCAG 2.2)
|
|
21
|
+
|
|
22
|
+
| 유형 | 최소 비율 | 예시 |
|
|
23
|
+
|------|----------|------|
|
|
24
|
+
| **일반 텍스트** | 4.5:1 | 14px body text |
|
|
25
|
+
| **큰 텍스트** | 3:1 | 18pt+ 또는 14pt bold+ |
|
|
26
|
+
| **Interactive 요소** | 3:1 | 버튼, 아이콘 |
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
// ✅ 충분한 대비
|
|
30
|
+
<button className="bg-blue-600 text-white">Save</button>
|
|
31
|
+
|
|
32
|
+
// ❌ 불충분한 대비
|
|
33
|
+
<button className="bg-gray-100 text-gray-300">Save</button>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
</color_contrast>
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
<keyboard_navigation>
|
|
41
|
+
|
|
42
|
+
## 키보드 네비게이션
|
|
43
|
+
|
|
44
|
+
| 키 | 동작 | 적용 |
|
|
45
|
+
|---|------|------|
|
|
46
|
+
| **Tab** | 다음 요소 | tabindex 순서 |
|
|
47
|
+
| **Enter/Space** | 활성화 | 버튼, 링크 |
|
|
48
|
+
| **Esc** | 취소/닫기 | 모달, 드롭다운 |
|
|
49
|
+
| **↑↓** | 선택 이동 | 드롭다운, 슬라이더 |
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
// ✅ 키보드 접근 가능
|
|
53
|
+
<button
|
|
54
|
+
tabIndex={0}
|
|
55
|
+
onKeyDown={(e) => {
|
|
56
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
57
|
+
handleClick()
|
|
58
|
+
}
|
|
59
|
+
}}
|
|
60
|
+
>
|
|
61
|
+
Click me
|
|
62
|
+
</button>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
</keyboard_navigation>
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
<aria_attributes>
|
|
70
|
+
|
|
71
|
+
## ARIA 속성
|
|
72
|
+
|
|
73
|
+
| 속성 | 용도 | 예시 |
|
|
74
|
+
|------|------|------|
|
|
75
|
+
| **role** | 역할 명시 | `role="navigation"` |
|
|
76
|
+
| **aria-label** | 레이블 제공 | `aria-label="Close menu"` |
|
|
77
|
+
| **aria-labelledby** | 레이블 ID 참조 | `aria-labelledby="title-id"` |
|
|
78
|
+
| **aria-expanded** | 확장 상태 | `aria-expanded={isOpen}` |
|
|
79
|
+
| **aria-live** | 동적 업데이트 | `aria-live="polite"` |
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
// ✅ ARIA 속성 사용
|
|
83
|
+
<button
|
|
84
|
+
aria-label="Close menu"
|
|
85
|
+
aria-expanded={isOpen}
|
|
86
|
+
aria-controls="menu-id"
|
|
87
|
+
>
|
|
88
|
+
<svg className="w-6 h-6" aria-hidden="true" />
|
|
89
|
+
</button>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
</aria_attributes>
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
<screen_readers>
|
|
97
|
+
|
|
98
|
+
## 스크린 리더 테스트
|
|
99
|
+
|
|
100
|
+
| 도구 | 플랫폼 | 용도 |
|
|
101
|
+
|------|--------|------|
|
|
102
|
+
| **VoiceOver** | macOS/iOS | Safari |
|
|
103
|
+
| **NVDA** | Windows | Firefox |
|
|
104
|
+
| **JAWS** | Windows | Chrome |
|
|
105
|
+
|
|
106
|
+
</screen_readers>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Design Checklist
|
|
2
|
+
|
|
3
|
+
<do>
|
|
4
|
+
|
|
5
|
+
## DO
|
|
6
|
+
|
|
7
|
+
| 항목 | 설명 |
|
|
8
|
+
|------|------|
|
|
9
|
+
| **사용자 중심** | Nielsen 휴리스틱, Don Norman 원칙 적용 |
|
|
10
|
+
| **접근성 우선** | WCAG 2.2 AA, 키보드/스크린 리더 지원 |
|
|
11
|
+
| **일관성** | 디자인 시스템, 패턴 재사용 |
|
|
12
|
+
| **반응형** | Mobile-First, 모든 Breakpoint 테스트 |
|
|
13
|
+
| **피드백** | Loading, Success, Error 상태 명확히 |
|
|
14
|
+
| **여백** | 8px 그리드, 충분한 breathing room |
|
|
15
|
+
| **대비** | 4.5:1 (일반), 3:1 (큰 텍스트) |
|
|
16
|
+
| **터치 타겟** | 44x44px 최소 (모바일) |
|
|
17
|
+
| **오류 예방** | 확인 다이얼로그, 입력 제약 |
|
|
18
|
+
| **테스트** | 실제 사용자, A/B 테스트 |
|
|
19
|
+
|
|
20
|
+
</do>
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
<dont>
|
|
25
|
+
|
|
26
|
+
## DON'T
|
|
27
|
+
|
|
28
|
+
| 항목 | 이유 |
|
|
29
|
+
|------|------|
|
|
30
|
+
| **색상만으로 정보 전달** | 색맹 사용자 고려 |
|
|
31
|
+
| **너무 많은 선택지** | Hick의 법칙 (7±2) |
|
|
32
|
+
| **작은 터치 타겟** | 손가락으로 클릭 어려움 |
|
|
33
|
+
| **자동 재생 미디어** | 접근성, 사용자 경험 저해 |
|
|
34
|
+
| **일관성 없는 패턴** | 학습 비용 증가 |
|
|
35
|
+
| **모호한 에러 메시지** | "오류 발생" 대신 구체적 설명 |
|
|
36
|
+
| **과도한 애니메이션** | 멀미, 성능 저하 |
|
|
37
|
+
| **플레이스홀더를 레이블로** | 입력 시 사라짐 |
|
|
38
|
+
| **지나친 혁신** | 표준 패턴 무시 |
|
|
39
|
+
| **접근성 무시** | 법적 문제, 사용자 배제 |
|
|
40
|
+
|
|
41
|
+
</dont>
|