sparkle-design-cli 1.5.0 → 1.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/README.md +11 -2
- package/bin/sparkle-design.js +9 -1
- package/lib/anti-pattern-rules.js +216 -1
- package/lib/generate-css.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -111,6 +111,14 @@ sparkle-design-cli check --help
|
|
|
111
111
|
- children なしの Button に prefixIcon / suffixIcon を使わない
|
|
112
112
|
- Material Symbols を className 直書きで使わない
|
|
113
113
|
- shadcn/ui 既定 token を Sparkle Design 内へ持ち込まない
|
|
114
|
+
- Tailwind デフォルト typography(text-sm, font-medium 等)を使わない
|
|
115
|
+
- CardTitle に typography を上書きしない
|
|
116
|
+
- CardControl に Button / IconButton 以外を入れない
|
|
117
|
+
- CardHeader / CardContent の padding を上書きしない
|
|
118
|
+
- asChild と prefixIcon / suffixIcon / isLoading を併用しない
|
|
119
|
+
- disabled を isDisabled の代わりに使わない
|
|
120
|
+
- Button の prefixIcon に JSX を渡さない
|
|
121
|
+
- Icon の children にテキストを渡さない
|
|
114
122
|
|
|
115
123
|
#### Manual Review Reminders
|
|
116
124
|
|
|
@@ -119,6 +127,7 @@ sparkle-design-cli check --help
|
|
|
119
127
|
- Badge と Tag の意味的な使い分け
|
|
120
128
|
- CardDescription の typography / color token 明示
|
|
121
129
|
- Dialog と Modal の UX 上の使い分け
|
|
130
|
+
- 手書き span が Tag で置き換え可能か
|
|
122
131
|
|
|
123
132
|
#### 導入先での推奨設定
|
|
124
133
|
|
|
@@ -181,8 +190,8 @@ npx --yes sparkle-design-cli setup --assistant generic
|
|
|
181
190
|
#### Core(プラグインが出力)
|
|
182
191
|
|
|
183
192
|
- `primary`: プライマリカラー(blue, red, orange など)
|
|
184
|
-
- `font-pro`:
|
|
185
|
-
- `font-mono`:
|
|
193
|
+
- `font-pro`: プロポーショナルフォント([Google Fonts](https://fonts.google.com/) の名前)
|
|
194
|
+
- `font-mono`: モノスペースフォント([Google Fonts](https://fonts.google.com/) の名前)
|
|
186
195
|
- `radius`: 角丸設定(sm, md, lg など)
|
|
187
196
|
|
|
188
197
|
### extend セクション(拡張設定)
|
package/bin/sparkle-design.js
CHANGED
|
@@ -186,7 +186,15 @@ Check で検出する主なパターン:
|
|
|
186
186
|
- DialogCancel / DialogAction を <Button> で二重ラップしている
|
|
187
187
|
- children なしの Button に prefixIcon / suffixIcon を使っている
|
|
188
188
|
- Material Symbols を className 直書きしている
|
|
189
|
-
- text-muted-foreground
|
|
189
|
+
- shadcn/ui 既定 token(text-muted-foreground 等)を持ち込んでいる
|
|
190
|
+
- Tailwind デフォルト typography(text-sm, font-medium 等)を使っている
|
|
191
|
+
- CardTitle に typography を上書きしている
|
|
192
|
+
- CardControl に Button / IconButton 以外を入れている
|
|
193
|
+
- CardHeader / CardContent の padding を上書きしている
|
|
194
|
+
- asChild と prefixIcon / suffixIcon / isLoading を併用している
|
|
195
|
+
- disabled を isDisabled の代わりに使っている
|
|
196
|
+
- Button の prefixIcon に JSX を渡している
|
|
197
|
+
- Icon の children にテキストを渡している
|
|
190
198
|
|
|
191
199
|
JSON 出力では manualReviewReminders も返すため、AI から実行する場合は --format json を推奨
|
|
192
200
|
`);
|
|
@@ -40,6 +40,11 @@ const MANUAL_REVIEW_REMINDERS = [
|
|
|
40
40
|
message:
|
|
41
41
|
'Dialog が確認用途、Modal が入力・詳細表示用途になっているか確認してください。文脈判断が必要なケースは機械検出されません。',
|
|
42
42
|
},
|
|
43
|
+
{
|
|
44
|
+
id: 'tag-vs-handwritten-span',
|
|
45
|
+
message:
|
|
46
|
+
'ステータスバッジ風の手書き span(bg-*-100 text-*-700 rounded-full 等)が Tag コンポーネントで置き換え可能か確認してください。',
|
|
47
|
+
},
|
|
43
48
|
];
|
|
44
49
|
|
|
45
50
|
const ANTI_PATTERN_GROUPS = [
|
|
@@ -859,10 +864,220 @@ const ANTI_PATTERN_GROUPS = [
|
|
|
859
864
|
},
|
|
860
865
|
],
|
|
861
866
|
},
|
|
867
|
+
{
|
|
868
|
+
id: 'tailwind-typography',
|
|
869
|
+
check: {
|
|
870
|
+
description: 'Tailwind デフォルト typography を Sparkle Design コンポーネント内で使わない',
|
|
871
|
+
recommendation:
|
|
872
|
+
'text-sm / text-xs / text-base / font-medium / font-semibold / font-bold は character-* utility に置き換えてください。',
|
|
873
|
+
pattern: /\b(text-(?:xs|sm|base|lg|xl|2xl)|font-(?:medium|semibold|bold|normal|light))\b/g,
|
|
874
|
+
},
|
|
875
|
+
featureSection: lines([
|
|
876
|
+
'### Tailwind デフォルト typography を使わない',
|
|
877
|
+
'',
|
|
878
|
+
'```tsx',
|
|
879
|
+
'// ✅ Correct — character-* utility を使う',
|
|
880
|
+
'<span className="character-3-regular-pro">テキスト</span>',
|
|
881
|
+
'',
|
|
882
|
+
'// ❌ Wrong — Tailwind デフォルトの typography を使わない',
|
|
883
|
+
'<span className="text-sm font-medium">テキスト</span>',
|
|
884
|
+
'```',
|
|
885
|
+
'',
|
|
886
|
+
'Sparkle Design コンポーネント内では `character-*-pro` / `character-*-mono` を使用する。Tailwind の `text-sm` / `font-medium` 等は使わない。',
|
|
887
|
+
]),
|
|
888
|
+
jsdocTargets: [],
|
|
889
|
+
},
|
|
890
|
+
{
|
|
891
|
+
id: 'card-title-typography',
|
|
892
|
+
check: {
|
|
893
|
+
description: 'CardTitle に typography 系クラスを付与しない',
|
|
894
|
+
recommendation:
|
|
895
|
+
'CardTitle は character-4-bold-pro を内蔵しています。className で typography を上書きしないでください。',
|
|
896
|
+
pattern: /<CardTitle\b[^>]*\bclassName\s*=\s*(?:"[^"]*\bcharacter-[^"]*"|'[^']*\bcharacter-[^']*'|\{[^}]*\bcharacter-[^}]*\})[^>]*>/g,
|
|
897
|
+
},
|
|
898
|
+
featureSection: lines([
|
|
899
|
+
'### CardTitle に typography を上書きしない',
|
|
900
|
+
'',
|
|
901
|
+
'```tsx',
|
|
902
|
+
'// ✅ Correct — CardTitle はデフォルトで character-4-bold-pro が適用される',
|
|
903
|
+
'<CardTitle>タイトル</CardTitle>',
|
|
904
|
+
'',
|
|
905
|
+
'// ❌ Wrong — typography を上書きしない',
|
|
906
|
+
'<CardTitle className="character-2-bold-pro">タイトル</CardTitle>',
|
|
907
|
+
'```',
|
|
908
|
+
]),
|
|
909
|
+
jsdocTargets: [],
|
|
910
|
+
},
|
|
911
|
+
{
|
|
912
|
+
id: 'card-control-non-button',
|
|
913
|
+
check: {
|
|
914
|
+
description: 'CardControl に Button / IconButton 以外を入れない',
|
|
915
|
+
recommendation:
|
|
916
|
+
'CardControl はアクションボタン用です。ステータス表示には CardDescription を使ってください。',
|
|
917
|
+
pattern: /<CardControl\b[^>]*>[\s\S]*?<(?!Button\b|IconButton\b|\/CardControl)\w+/g,
|
|
918
|
+
},
|
|
919
|
+
featureSection: lines([
|
|
920
|
+
'### CardControl にはアクションボタンのみを入れる',
|
|
921
|
+
'',
|
|
922
|
+
'```tsx',
|
|
923
|
+
'// ✅ Correct — Button / IconButton を CardControl に入れる',
|
|
924
|
+
'<CardControl>',
|
|
925
|
+
' <Button>保存</Button>',
|
|
926
|
+
'</CardControl>',
|
|
927
|
+
'',
|
|
928
|
+
'// ❌ Wrong — ステータス表示を CardControl に入れない(CardDescription を使う)',
|
|
929
|
+
'<CardControl>',
|
|
930
|
+
' <Tag status="negative">警告</Tag>',
|
|
931
|
+
'</CardControl>',
|
|
932
|
+
'```',
|
|
933
|
+
]),
|
|
934
|
+
jsdocTargets: [
|
|
935
|
+
{
|
|
936
|
+
file: 'src/components/ui/card/index.tsx',
|
|
937
|
+
targetName: 'CardControl',
|
|
938
|
+
section: {
|
|
939
|
+
bullets: [
|
|
940
|
+
{
|
|
941
|
+
ja: 'CardControl にはアクション用の Button / IconButton のみを入れてください。ステータス表示(Tag 等)は CardDescription に入れてください。',
|
|
942
|
+
en: 'CardControl is for action buttons (Button / IconButton) only. Place status displays (Tag, etc.) in CardDescription.',
|
|
943
|
+
},
|
|
944
|
+
],
|
|
945
|
+
example: lines([
|
|
946
|
+
'// ✅ Correct',
|
|
947
|
+
'<CardControl>',
|
|
948
|
+
' <Button theme="neutral" variant="outline">キャンセル</Button>',
|
|
949
|
+
' <Button>保存</Button>',
|
|
950
|
+
'</CardControl>',
|
|
951
|
+
'',
|
|
952
|
+
'// ❌ Wrong - ステータス表示を CardControl に入れない',
|
|
953
|
+
'<CardControl>',
|
|
954
|
+
' <Tag status="negative">警告</Tag>',
|
|
955
|
+
'</CardControl>',
|
|
956
|
+
]),
|
|
957
|
+
},
|
|
958
|
+
},
|
|
959
|
+
],
|
|
960
|
+
},
|
|
961
|
+
{
|
|
962
|
+
id: 'card-padding-override',
|
|
963
|
+
check: {
|
|
964
|
+
description: 'Card 系コンポーネントのデフォルト padding を安易に上書きしない',
|
|
965
|
+
recommendation:
|
|
966
|
+
'CardHeader / CardContent のデフォルト padding(px-6 py-2)をそのまま使ってください。上書きは本当に必要な場合のみ。',
|
|
967
|
+
pattern: /<Card(?:Header|Content)\b[^>]*\bclassName\s*=\s*(?:"[^"]*\b(?:p-|px-|py-|pt-|pb-|pl-|pr-)[^"]*"|'[^']*\b(?:p-|px-|py-|pt-|pb-|pl-|pr-)[^']*'|\{[^}]*\b(?:p-|px-|py-|pt-|pb-|pl-|pr-)[^}]*\})[^>]*>/g,
|
|
968
|
+
},
|
|
969
|
+
featureSection: lines([
|
|
970
|
+
'### Card 系コンポーネントの padding を上書きしない',
|
|
971
|
+
'',
|
|
972
|
+
'```tsx',
|
|
973
|
+
'// ✅ Correct — デフォルトの padding をそのまま使う',
|
|
974
|
+
'<CardHeader>',
|
|
975
|
+
' <CardTitle>タイトル</CardTitle>',
|
|
976
|
+
'</CardHeader>',
|
|
977
|
+
'',
|
|
978
|
+
'// ❌ Wrong — padding を安易に上書きしない',
|
|
979
|
+
'<CardHeader className="p-4 pb-2">',
|
|
980
|
+
' <CardTitle>タイトル</CardTitle>',
|
|
981
|
+
'</CardHeader>',
|
|
982
|
+
'```',
|
|
983
|
+
]),
|
|
984
|
+
jsdocTargets: [],
|
|
985
|
+
},
|
|
986
|
+
{
|
|
987
|
+
id: 'aschild-with-icon-props',
|
|
988
|
+
check: {
|
|
989
|
+
description: 'asChild 使用時に prefixIcon / suffixIcon / isLoading を使わない',
|
|
990
|
+
recommendation:
|
|
991
|
+
'asChild モードでは prefixIcon / suffixIcon / isLoading は無視されます。アイコン付きの Link が必要なら asChild を外してください。',
|
|
992
|
+
pattern: /<Button\b(?=[^>]*\basChild\b)(?=[^>]*\b(?:prefixIcon|suffixIcon|isLoading)\b)[^>]*>/g,
|
|
993
|
+
},
|
|
994
|
+
featureSection: lines([
|
|
995
|
+
'### asChild と prefixIcon / suffixIcon / isLoading を併用しない',
|
|
996
|
+
'',
|
|
997
|
+
'```tsx',
|
|
998
|
+
'// ✅ Correct',
|
|
999
|
+
'<Button asChild>',
|
|
1000
|
+
' <Link href="/items/new">新規作成</Link>',
|
|
1001
|
+
'</Button>',
|
|
1002
|
+
'',
|
|
1003
|
+
'// ❌ Wrong — asChild では prefixIcon は反映されない',
|
|
1004
|
+
'<Button asChild prefixIcon="add">',
|
|
1005
|
+
' <Link href="/items/new">新規作成</Link>',
|
|
1006
|
+
'</Button>',
|
|
1007
|
+
'```',
|
|
1008
|
+
]),
|
|
1009
|
+
jsdocTargets: [],
|
|
1010
|
+
},
|
|
1011
|
+
{
|
|
1012
|
+
id: 'disabled-vs-is-disabled',
|
|
1013
|
+
check: {
|
|
1014
|
+
description: 'Sparkle Design コンポーネントでは isDisabled を使う',
|
|
1015
|
+
recommendation:
|
|
1016
|
+
'Button / Input / Checkbox / IconButton では disabled ではなく isDisabled を使ってください。',
|
|
1017
|
+
pattern: /<(Button|Input|Checkbox|IconButton)\b[^>]*\sdisabled(?!=\s*\{)\b/g,
|
|
1018
|
+
},
|
|
1019
|
+
featureSection: lines([
|
|
1020
|
+
'### disabled ではなく isDisabled を使う',
|
|
1021
|
+
'',
|
|
1022
|
+
'```tsx',
|
|
1023
|
+
'// ✅ Correct',
|
|
1024
|
+
'<Button isDisabled>確定</Button>',
|
|
1025
|
+
'<Input isDisabled placeholder="無効状態" />',
|
|
1026
|
+
'',
|
|
1027
|
+
'// ❌ Wrong — disabled 属性を直接使わない',
|
|
1028
|
+
'<Button disabled>確定</Button>',
|
|
1029
|
+
'```',
|
|
1030
|
+
'',
|
|
1031
|
+
'HTML 標準の `disabled` も互換のため受け付けますが、Sparkle Design のコードでは `isDisabled` に統一します。',
|
|
1032
|
+
]),
|
|
1033
|
+
jsdocTargets: [],
|
|
1034
|
+
},
|
|
1035
|
+
{
|
|
1036
|
+
id: 'button-prefixicon-jsx',
|
|
1037
|
+
check: {
|
|
1038
|
+
description: 'Button の prefixIcon / suffixIcon に JSX を渡さない',
|
|
1039
|
+
recommendation:
|
|
1040
|
+
'prefixIcon / suffixIcon には Material Symbols のアイコン名(文字列)を渡してください。JSX(<Icon ... />)は渡さないでください。',
|
|
1041
|
+
pattern: /<Button\b[^>]*\b(?:prefixIcon|suffixIcon)\s*=\s*\{[^}]*</g,
|
|
1042
|
+
},
|
|
1043
|
+
featureSection: lines([
|
|
1044
|
+
'### Button の prefixIcon / suffixIcon に JSX を渡さない',
|
|
1045
|
+
'',
|
|
1046
|
+
'```tsx',
|
|
1047
|
+
'// ✅ Correct — 文字列でアイコン名を渡す',
|
|
1048
|
+
'<Button prefixIcon="bolt">アクション</Button>',
|
|
1049
|
+
'',
|
|
1050
|
+
'// ❌ Wrong — JSX を渡さない',
|
|
1051
|
+
'<Button prefixIcon={<Icon icon="bolt" size={4} />}>アクション</Button>',
|
|
1052
|
+
'```',
|
|
1053
|
+
]),
|
|
1054
|
+
jsdocTargets: [],
|
|
1055
|
+
},
|
|
1056
|
+
{
|
|
1057
|
+
id: 'icon-children-text',
|
|
1058
|
+
check: {
|
|
1059
|
+
description: 'Icon の children にテキストを渡さない',
|
|
1060
|
+
recommendation:
|
|
1061
|
+
'Icon には icon prop でアイコン名を渡してください。children にテキストを渡すのは旧 Material Icons の書き方です。',
|
|
1062
|
+
pattern: /<Icon\b(?:[^>]|\/(?!>))*>[^<]+<\/Icon>/g,
|
|
1063
|
+
},
|
|
1064
|
+
featureSection: lines([
|
|
1065
|
+
'### Icon の children にテキストを渡さない',
|
|
1066
|
+
'',
|
|
1067
|
+
'```tsx',
|
|
1068
|
+
'// ✅ Correct — icon prop を使う',
|
|
1069
|
+
'<Icon icon="chevron_left" size={4} />',
|
|
1070
|
+
'',
|
|
1071
|
+
'// ❌ Wrong — children にテキストを渡さない',
|
|
1072
|
+
'<Icon size={4}>chevron_left</Icon>',
|
|
1073
|
+
'```',
|
|
1074
|
+
]),
|
|
1075
|
+
jsdocTargets: [],
|
|
1076
|
+
},
|
|
862
1077
|
];
|
|
863
1078
|
|
|
864
1079
|
function getCheckRules() {
|
|
865
|
-
const order = ['dialog-form', 'dialog-button-wrap', 'button-icon-only', 'material-symbols-direct', 'shadcn-token'];
|
|
1080
|
+
const order = ['dialog-form', 'dialog-button-wrap', 'button-icon-only', 'material-symbols-direct', 'shadcn-token', 'tailwind-typography', 'card-title-typography', 'card-control-non-button', 'card-padding-override', 'aschild-with-icon-props', 'disabled-vs-is-disabled', 'button-prefixicon-jsx', 'icon-children-text'];
|
|
866
1081
|
|
|
867
1082
|
return ANTI_PATTERN_GROUPS.filter((group) => group.check)
|
|
868
1083
|
.map((group) => ({
|
package/lib/generate-css.js
CHANGED
|
@@ -212,7 +212,7 @@ function processTemplate(template, config, grayMapping, radiusMapping, colors) {
|
|
|
212
212
|
processedCSS = processedCSS.replace(/\{\{FONT_FAMILY_PRO\}\}/g, proFamilyValue);
|
|
213
213
|
processedCSS = processedCSS.replace(/\{\{FONT_FAMILY_MONO\}\}/g, monoFamilyValue);
|
|
214
214
|
|
|
215
|
-
// 5.
|
|
215
|
+
// 5. 基本的な設定値による置換(オブジェクト・配列・拡張フィールドはスキップ)
|
|
216
216
|
Object.entries(config).forEach(([key, value]) => {
|
|
217
217
|
// 配列・オブジェクト・拡張フィールドはスキップ
|
|
218
218
|
if (Array.isArray(value) || (typeof value === 'object' && value !== null) || key === 'custom-css' || key === 'fonts' || key === 'extend' || key === 'source-packages') {
|