@stackonward/cli 1.6.0 → 1.7.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/dist/index.js +1 -1
- package/dist/resources/components/nuxt/site-source/app/assets/css/site-shell.css +14 -2
- package/dist/resources/components/nuxt/site-source/app/components/content/CallToAction.vue +3 -1
- package/dist/resources/components/nuxt/site-source/app/components/content/Faq.vue +2 -2
- package/dist/resources/components/nuxt/site-source/app/components/content/Reviews.vue +2 -2
- package/dist/resources/components/nuxt/site-source/app/components/steps/Showcase.vue +2 -2
- package/dist/resources/components/nuxt/site-source/app/theme/button.ts +18 -22
- package/dist/resources/components/nuxt/site-source/app/theme/index.ts +24 -0
- package/dist/resources/components/nuxt/site-source/app/theme/input.ts +32 -17
- package/dist/resources/components/nuxt/site-source/app/theme/modal.ts +1 -1
- package/dist/resources/components/nuxt/site-source/app/theme/select.ts +62 -18
- package/dist/resources/components/nuxt/site-source/app/theme/textarea.ts +73 -1
- package/dist/resources/registry.json +1 -1
- package/dist/resources/templates/nuxt/.claude/docs/stack.md +7 -1
- package/dist/resources/templates/nuxt/DESIGN.md +54 -9
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -19313,7 +19313,7 @@ function registerDeploy(program2) {
|
|
|
19313
19313
|
|
|
19314
19314
|
// src/index.ts
|
|
19315
19315
|
var program = new Command();
|
|
19316
|
-
program.name("stackonward").description("Create projects and install reusable components and snippets").version("1.
|
|
19316
|
+
program.name("stackonward").description("Create projects and install reusable components and snippets").version("1.7.0");
|
|
19317
19317
|
program.command("init [name]").description("Create a new project from template").option("-t, --template <template>", "Template to use").option("-d, --dir <directory>", "Target directory").option("--skip-install", "Skip dependency installation").option("--skip-git", "Skip git initialization").option("--module <module>", "Go module path (required for go-backend)").option("--author <author>", "Author name").option("-y, --yes", "Use defaults without interactive prompts").action(init);
|
|
19318
19318
|
program.command("add [items...]").description("Add components or snippets to your project").option("-c, --component", "Add component(s)").option("-s, --snippet", "Add snippet(s)").option("-a, --all", "Add all items in category").option("-y, --yes", "Skip confirmation prompts").option("-o, --overwrite", "Overwrite existing files").option("-p, --path <path>", "Custom install path").action(add);
|
|
19319
19319
|
program.command("list [type]").description("List available templates, components, or snippets").option("-t, --templates", "List templates").option("-c, --components", "List components").option("-s, --snippets", "List snippets").action(list);
|
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
@import "@nuxt/ui";
|
|
3
3
|
|
|
4
4
|
@theme inline {
|
|
5
|
+
--spacing-control-xs: var(--site-control-height-xs);
|
|
6
|
+
--spacing-control-sm: var(--site-control-height-sm);
|
|
7
|
+
--spacing-control-md: var(--site-control-height-md);
|
|
8
|
+
--spacing-control-lg: var(--site-control-height-lg);
|
|
9
|
+
--spacing-control-xl: var(--site-control-height-xl);
|
|
10
|
+
--spacing-control-2xl: var(--site-control-height-2xl);
|
|
5
11
|
--spacing-icon-xs: var(--site-icon-xs);
|
|
6
12
|
--spacing-icon-sm: var(--site-icon-sm);
|
|
7
13
|
--spacing-icon-md: var(--site-icon-md);
|
|
@@ -36,8 +42,8 @@
|
|
|
36
42
|
--color-dimmed: var(--site-color-dimmed);
|
|
37
43
|
--color-border: var(--site-color-border);
|
|
38
44
|
--color-border-strong: var(--site-color-border-strong);
|
|
39
|
-
--radius-ml:
|
|
40
|
-
--radius-2ml:
|
|
45
|
+
--radius-ml: var(--site-radius-md);
|
|
46
|
+
--radius-2ml: var(--site-radius-lg);
|
|
41
47
|
--shadow-accordion:
|
|
42
48
|
0 4px 16px color-mix(in srgb, var(--site-color-border) 40%, transparent),
|
|
43
49
|
0 11px 24px color-mix(in srgb, var(--site-color-border) 20%, transparent);
|
|
@@ -49,6 +55,12 @@
|
|
|
49
55
|
}
|
|
50
56
|
|
|
51
57
|
:root {
|
|
58
|
+
--site-control-height-xs: 2rem;
|
|
59
|
+
--site-control-height-sm: 2.25rem;
|
|
60
|
+
--site-control-height-md: 2.625rem;
|
|
61
|
+
--site-control-height-lg: 3rem;
|
|
62
|
+
--site-control-height-xl: 3.25rem;
|
|
63
|
+
--site-control-height-2xl: 3.5rem;
|
|
52
64
|
--site-icon-xs: 0.75rem;
|
|
53
65
|
--site-icon-sm: 1rem;
|
|
54
66
|
--site-icon-md: 1.25rem;
|
|
@@ -34,7 +34,9 @@ defineProps<{
|
|
|
34
34
|
{{ description }}
|
|
35
35
|
</p>
|
|
36
36
|
<slot name="action"
|
|
37
|
-
><UiButton :to="to" size="
|
|
37
|
+
><UiButton :to="to" size="2xl" class="min-w-[200px] max-w-full">{{
|
|
38
|
+
buttonLabel
|
|
39
|
+
}}</UiButton></slot
|
|
38
40
|
>
|
|
39
41
|
</div>
|
|
40
42
|
<UiImage
|
|
@@ -46,8 +46,8 @@ const columns = computed(() =>
|
|
|
46
46
|
<UiButton
|
|
47
47
|
v-if="buttonLabel"
|
|
48
48
|
:to="to"
|
|
49
|
-
size="
|
|
50
|
-
class="mx-auto mt-12.5 flex max-md:mt-7.5"
|
|
49
|
+
size="2xl"
|
|
50
|
+
class="w-fit min-w-[200px] max-w-full mx-auto mt-12.5 flex max-md:mt-7.5"
|
|
51
51
|
@click="emit('button-click', $event)"
|
|
52
52
|
>{{ buttonLabel }}</UiButton
|
|
53
53
|
>
|
|
@@ -144,9 +144,9 @@ onMounted(resumeAutoplay);
|
|
|
144
144
|
<slot name="footer">
|
|
145
145
|
<UiButton
|
|
146
146
|
v-if="showButton"
|
|
147
|
-
size="
|
|
147
|
+
size="2xl"
|
|
148
148
|
:label="buttonText"
|
|
149
|
-
class="flex justify-center mt-10 mx-auto max-lg:mt-0"
|
|
149
|
+
class="w-fit min-w-[200px] max-w-full flex justify-center mt-10 mx-auto max-lg:mt-0"
|
|
150
150
|
ripple
|
|
151
151
|
:to="to"
|
|
152
152
|
/>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
slots: {
|
|
3
|
-
base: "border-0 rounded-ml font-medium
|
|
3
|
+
base: "border-0 rounded-ml font-medium inline-flex items-center justify-center cursor-pointer disabled:cursor-not-allowed aria-disabled:cursor-not-allowed disabled:opacity-75 aria-disabled:opacity-75 transition-colors",
|
|
4
4
|
label: "truncate",
|
|
5
5
|
leadingIcon: "shrink-0",
|
|
6
6
|
leadingAvatar: "shrink-0",
|
|
@@ -13,44 +13,39 @@ export default {
|
|
|
13
13
|
vertical:
|
|
14
14
|
"not-only:first:rounded-b-none not-only:last:rounded-t-none not-last:not-first:rounded-none",
|
|
15
15
|
},
|
|
16
|
-
variant: {
|
|
17
|
-
link: "h-auto min-w-0 p-0",
|
|
18
|
-
ghost: "min-w-0",
|
|
19
|
-
},
|
|
20
|
-
color: { neutral: "min-w-0" },
|
|
21
16
|
size: {
|
|
22
17
|
xs: {
|
|
23
|
-
base: "px-3 py-
|
|
18
|
+
base: "px-3 py-0 text-xs/4 h-control-xs gap-1.5",
|
|
24
19
|
leadingIcon: "size-icon-sm",
|
|
25
20
|
leadingAvatarSize: "3xs",
|
|
26
21
|
trailingIcon: "size-icon-sm",
|
|
27
22
|
},
|
|
28
23
|
sm: {
|
|
29
|
-
base: "px-4 py-
|
|
24
|
+
base: "px-4 py-0 text-sm/5 h-control-sm gap-1.5",
|
|
30
25
|
leadingIcon: "size-icon-sm",
|
|
31
26
|
leadingAvatarSize: "3xs",
|
|
32
27
|
trailingIcon: "size-icon-sm",
|
|
33
28
|
},
|
|
34
29
|
md: {
|
|
35
|
-
base: "px-5 py-
|
|
30
|
+
base: "px-5 py-0 text-sm/5 h-control-md gap-2",
|
|
36
31
|
leadingIcon: "size-icon-md",
|
|
37
32
|
leadingAvatarSize: "2xs",
|
|
38
33
|
trailingIcon: "size-icon-md",
|
|
39
34
|
},
|
|
40
35
|
lg: {
|
|
41
|
-
base: "px-6 py-
|
|
36
|
+
base: "px-6 py-0 text-base/6 h-control-lg gap-2",
|
|
42
37
|
leadingIcon: "size-icon-md",
|
|
43
38
|
leadingAvatarSize: "2xs",
|
|
44
39
|
trailingIcon: "size-icon-md",
|
|
45
40
|
},
|
|
46
41
|
xl: {
|
|
47
|
-
base: "px-7 py-
|
|
42
|
+
base: "px-7 py-0 text-lg/7 h-control-xl gap-2",
|
|
48
43
|
leadingIcon: "size-icon-lg",
|
|
49
44
|
leadingAvatarSize: "xs",
|
|
50
45
|
trailingIcon: "size-icon-lg",
|
|
51
46
|
},
|
|
52
47
|
"2xl": {
|
|
53
|
-
base: "px-8 py-
|
|
48
|
+
base: "px-8 py-0 text-lg/7 h-control-2xl gap-2",
|
|
54
49
|
leadingIcon: "size-icon-lg",
|
|
55
50
|
leadingAvatarSize: "sm",
|
|
56
51
|
trailingIcon: "size-icon-lg",
|
|
@@ -77,9 +72,9 @@ export default {
|
|
|
77
72
|
color: "neutral",
|
|
78
73
|
variant: "outline",
|
|
79
74
|
class:
|
|
80
|
-
"border-0 ring-1 ring-inset ring-border bg-surface text-text hover:bg-surface-muted focus-visible:ring-primary focus-visible:outline-
|
|
75
|
+
"border-0 ring-1 ring-inset ring-border bg-surface text-text hover:bg-surface-muted focus-visible:ring-primary focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary",
|
|
81
76
|
},
|
|
82
|
-
{ size: "2xl", square: true, class: "w-
|
|
77
|
+
{ size: "2xl", square: true, class: "w-control-2xl p-0" },
|
|
83
78
|
{
|
|
84
79
|
color: "primary",
|
|
85
80
|
variant: "solid",
|
|
@@ -90,7 +85,7 @@ export default {
|
|
|
90
85
|
color: "primary",
|
|
91
86
|
variant: "outline",
|
|
92
87
|
class:
|
|
93
|
-
"text-primary-hover ring-
|
|
88
|
+
"text-primary-hover ring-1 ring-inset ring-primary hover:bg-primary hover:text-primary-contrast active:transition-none active:ring-primary-hover active:bg-primary-hover active:text-primary-contrast focus:outline-none focus:transition-none focus-visible:ring-2 focus-visible:ring-primary-hover",
|
|
94
89
|
},
|
|
95
90
|
{
|
|
96
91
|
color: "primary",
|
|
@@ -126,7 +121,7 @@ export default {
|
|
|
126
121
|
color: "success",
|
|
127
122
|
variant: "outline",
|
|
128
123
|
class:
|
|
129
|
-
"text-success-hover ring-
|
|
124
|
+
"text-success-hover ring-1 ring-inset ring-success hover:bg-success hover:text-success-contrast active:transition-none active:ring-success-hover active:bg-success-hover active:text-success-contrast focus:outline-none focus:transition-none focus-visible:ring-2 focus-visible:ring-success-hover",
|
|
130
125
|
},
|
|
131
126
|
{
|
|
132
127
|
color: "success",
|
|
@@ -155,27 +150,27 @@ export default {
|
|
|
155
150
|
{
|
|
156
151
|
size: "xs",
|
|
157
152
|
square: true,
|
|
158
|
-
class: "w-
|
|
153
|
+
class: "w-control-xs p-0",
|
|
159
154
|
},
|
|
160
155
|
{
|
|
161
156
|
size: "sm",
|
|
162
157
|
square: true,
|
|
163
|
-
class: "w-
|
|
158
|
+
class: "w-control-sm p-0",
|
|
164
159
|
},
|
|
165
160
|
{
|
|
166
161
|
size: "md",
|
|
167
162
|
square: true,
|
|
168
|
-
class: "w-
|
|
163
|
+
class: "w-control-md p-0",
|
|
169
164
|
},
|
|
170
165
|
{
|
|
171
166
|
size: "lg",
|
|
172
167
|
square: true,
|
|
173
|
-
class: "w-
|
|
168
|
+
class: "w-control-lg p-0",
|
|
174
169
|
},
|
|
175
170
|
{
|
|
176
171
|
size: "xl",
|
|
177
172
|
square: true,
|
|
178
|
-
class: "w-
|
|
173
|
+
class: "w-control-xl p-0",
|
|
179
174
|
},
|
|
180
175
|
{
|
|
181
176
|
loading: true,
|
|
@@ -192,10 +187,11 @@ export default {
|
|
|
192
187
|
trailingIcon: "animate-spin",
|
|
193
188
|
},
|
|
194
189
|
},
|
|
190
|
+
{ variant: "link", class: "h-auto min-w-0 p-0" },
|
|
195
191
|
{ square: true, class: "min-h-11 min-w-11" },
|
|
196
192
|
],
|
|
197
193
|
defaultVariants: {
|
|
198
|
-
color: "
|
|
194
|
+
color: "primary",
|
|
199
195
|
variant: "solid",
|
|
200
196
|
size: "md",
|
|
201
197
|
},
|
|
@@ -16,6 +16,30 @@ import textarea from "./textarea";
|
|
|
16
16
|
import tooltip from "./tooltip";
|
|
17
17
|
|
|
18
18
|
const siteUiTheme = {
|
|
19
|
+
tv: {
|
|
20
|
+
twMergeConfig: {
|
|
21
|
+
extend: {
|
|
22
|
+
theme: {
|
|
23
|
+
radius: ["ml", "2ml"],
|
|
24
|
+
spacing: [
|
|
25
|
+
"control-xs",
|
|
26
|
+
"control-sm",
|
|
27
|
+
"control-md",
|
|
28
|
+
"control-lg",
|
|
29
|
+
"control-xl",
|
|
30
|
+
"control-2xl",
|
|
31
|
+
"icon-xs",
|
|
32
|
+
"icon-sm",
|
|
33
|
+
"icon-md",
|
|
34
|
+
"icon-lg",
|
|
35
|
+
"icon-xl",
|
|
36
|
+
"icon-2xl",
|
|
37
|
+
"icon-3xl",
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
19
43
|
accordion,
|
|
20
44
|
breadcrumb,
|
|
21
45
|
button,
|
|
@@ -1,46 +1,46 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
slots: {
|
|
3
3
|
root: "w-full ring-border",
|
|
4
|
-
base:
|
|
4
|
+
base: "border-0 rounded-ml bg-surface text-text transition-colors",
|
|
5
5
|
},
|
|
6
6
|
variants: {
|
|
7
7
|
size: {
|
|
8
8
|
xs: {
|
|
9
|
-
base: "px-
|
|
10
|
-
leading: "ps-
|
|
11
|
-
trailing: "pe-
|
|
9
|
+
base: "h-control-xs px-3 py-0 text-xs/4 gap-2",
|
|
10
|
+
leading: "ps-3",
|
|
11
|
+
trailing: "pe-3",
|
|
12
12
|
leadingIcon: "size-icon-sm",
|
|
13
13
|
leadingAvatarSize: "3xs",
|
|
14
14
|
trailingIcon: "size-icon-sm",
|
|
15
15
|
},
|
|
16
16
|
sm: {
|
|
17
|
-
base: "px-
|
|
18
|
-
leading: "ps-
|
|
19
|
-
trailing: "pe-
|
|
17
|
+
base: "h-control-sm px-3 py-0 text-sm/5 gap-2",
|
|
18
|
+
leading: "ps-3",
|
|
19
|
+
trailing: "pe-3",
|
|
20
20
|
leadingIcon: "size-icon-sm",
|
|
21
21
|
leadingAvatarSize: "3xs",
|
|
22
22
|
trailingIcon: "size-icon-sm",
|
|
23
23
|
},
|
|
24
24
|
md: {
|
|
25
|
-
base: "px-
|
|
26
|
-
leading: "ps-
|
|
27
|
-
trailing: "pe-
|
|
25
|
+
base: "h-control-md px-3.5 py-0 text-sm/5 gap-2",
|
|
26
|
+
leading: "ps-3.5",
|
|
27
|
+
trailing: "pe-3.5",
|
|
28
28
|
leadingIcon: "size-icon-md",
|
|
29
29
|
leadingAvatarSize: "2xs",
|
|
30
30
|
trailingIcon: "size-icon-md",
|
|
31
31
|
},
|
|
32
32
|
lg: {
|
|
33
|
-
base: "px-
|
|
34
|
-
leading: "ps-
|
|
35
|
-
trailing: "pe-
|
|
33
|
+
base: "h-control-lg px-4 py-0 text-base/6 gap-2",
|
|
34
|
+
leading: "ps-4",
|
|
35
|
+
trailing: "pe-4",
|
|
36
36
|
leadingIcon: "size-icon-md",
|
|
37
37
|
leadingAvatarSize: "2xs",
|
|
38
38
|
trailingIcon: "size-icon-md",
|
|
39
39
|
},
|
|
40
40
|
xl: {
|
|
41
|
-
base: "px-
|
|
42
|
-
leading: "ps-
|
|
43
|
-
trailing: "pe-
|
|
41
|
+
base: "h-control-xl px-4.5 py-0 text-lg/7 gap-2",
|
|
42
|
+
leading: "ps-4.5",
|
|
43
|
+
trailing: "pe-4.5",
|
|
44
44
|
leadingIcon: "size-icon-lg",
|
|
45
45
|
leadingAvatarSize: "xs",
|
|
46
46
|
trailingIcon: "size-icon-lg",
|
|
@@ -52,8 +52,23 @@ export default {
|
|
|
52
52
|
color: "primary",
|
|
53
53
|
variant: ["outline", "subtle"],
|
|
54
54
|
class:
|
|
55
|
-
"ring-
|
|
55
|
+
"ring-1 ring-inset ring-border focus-visible:ring-primary focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary",
|
|
56
56
|
},
|
|
57
|
+
{ leading: true, size: "xs", class: "ps-9" },
|
|
58
|
+
{ trailing: true, size: "xs", class: "pe-9" },
|
|
59
|
+
{ fixed: false, size: "xs", class: "max-md:text-base md:text-xs" },
|
|
60
|
+
{ leading: true, size: "sm", class: "ps-9" },
|
|
61
|
+
{ trailing: true, size: "sm", class: "pe-9" },
|
|
62
|
+
{ fixed: false, size: "sm", class: "max-md:text-base md:text-sm" },
|
|
63
|
+
{ leading: true, size: "md", class: "ps-10.5" },
|
|
64
|
+
{ trailing: true, size: "md", class: "pe-10.5" },
|
|
65
|
+
{ fixed: false, size: "md", class: "max-md:text-base md:text-sm" },
|
|
66
|
+
{ leading: true, size: "lg", class: "ps-11" },
|
|
67
|
+
{ trailing: true, size: "lg", class: "pe-11" },
|
|
68
|
+
{ fixed: false, size: "lg", class: "max-md:text-base md:text-base" },
|
|
69
|
+
{ leading: true, size: "xl", class: "ps-12.5" },
|
|
70
|
+
{ trailing: true, size: "xl", class: "pe-12.5" },
|
|
71
|
+
{ fixed: false, size: "xl", class: "max-md:text-base md:text-lg" },
|
|
57
72
|
],
|
|
58
73
|
defaultVariants: {
|
|
59
74
|
size: "md",
|
|
@@ -1,31 +1,75 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
slots: {
|
|
3
|
-
base:
|
|
4
|
-
value: "
|
|
5
|
-
placeholder: "text-dimmed
|
|
6
|
-
group: "p-
|
|
7
|
-
content: "border-0 rounded-ml ring-1 ring-border bg-surface
|
|
8
|
-
item: [
|
|
9
|
-
"rounded-ml !py-3.5 !px-2.5 text-muted bg-transparent data-highlighted:text-muted hover:bg-surface data-[state=checked]:bg-surface",
|
|
10
|
-
],
|
|
3
|
+
base: "border-0 rounded-ml bg-surface text-text",
|
|
4
|
+
value: "truncate",
|
|
5
|
+
placeholder: "truncate text-dimmed",
|
|
6
|
+
group: "p-1.5",
|
|
7
|
+
content: "border-0 rounded-ml ring-1 ring-border bg-surface",
|
|
8
|
+
item: "min-h-11 rounded-ml px-3 py-2 text-sm/5 text-text data-highlighted:bg-surface-muted data-[state=checked]:bg-surface-muted",
|
|
11
9
|
trailingIcon:
|
|
12
10
|
"group-data-[state=open]:rotate-180 transition-transform duration-200 text-dimmed",
|
|
13
|
-
|
|
11
|
+
},
|
|
12
|
+
variants: {
|
|
13
|
+
size: {
|
|
14
|
+
xs: {
|
|
15
|
+
base: "h-control-xs px-3 py-0 text-xs/4 gap-2",
|
|
16
|
+
leading: "ps-3",
|
|
17
|
+
trailing: "pe-3",
|
|
18
|
+
leadingIcon: "size-icon-sm",
|
|
19
|
+
leadingAvatarSize: "3xs",
|
|
20
|
+
trailingIcon: "size-icon-sm",
|
|
21
|
+
},
|
|
22
|
+
sm: {
|
|
23
|
+
base: "h-control-sm px-3 py-0 text-sm/5 gap-2",
|
|
24
|
+
leading: "ps-3",
|
|
25
|
+
trailing: "pe-3",
|
|
26
|
+
leadingIcon: "size-icon-sm",
|
|
27
|
+
leadingAvatarSize: "3xs",
|
|
28
|
+
trailingIcon: "size-icon-sm",
|
|
29
|
+
},
|
|
30
|
+
md: {
|
|
31
|
+
base: "h-control-md px-3.5 py-0 text-sm/5 gap-2",
|
|
32
|
+
leading: "ps-3.5",
|
|
33
|
+
trailing: "pe-3.5",
|
|
34
|
+
leadingIcon: "size-icon-md",
|
|
35
|
+
leadingAvatarSize: "2xs",
|
|
36
|
+
trailingIcon: "size-icon-md",
|
|
37
|
+
},
|
|
38
|
+
lg: {
|
|
39
|
+
base: "h-control-lg px-4 py-0 text-base/6 gap-2",
|
|
40
|
+
leading: "ps-4",
|
|
41
|
+
trailing: "pe-4",
|
|
42
|
+
leadingIcon: "size-icon-md",
|
|
43
|
+
leadingAvatarSize: "2xs",
|
|
44
|
+
trailingIcon: "size-icon-md",
|
|
45
|
+
},
|
|
46
|
+
xl: {
|
|
47
|
+
base: "h-control-xl px-4.5 py-0 text-lg/7 gap-2",
|
|
48
|
+
leading: "ps-4.5",
|
|
49
|
+
trailing: "pe-4.5",
|
|
50
|
+
leadingIcon: "size-icon-lg",
|
|
51
|
+
leadingAvatarSize: "xs",
|
|
52
|
+
trailingIcon: "size-icon-lg",
|
|
53
|
+
},
|
|
54
|
+
},
|
|
14
55
|
},
|
|
15
56
|
compoundVariants: [
|
|
16
57
|
{
|
|
17
58
|
color: "primary",
|
|
18
59
|
variant: ["outline", "subtle"],
|
|
19
|
-
class:
|
|
20
|
-
|
|
21
|
-
{
|
|
22
|
-
leading: true,
|
|
23
|
-
class: { value: "ps-10", placeholder: "ps-10" },
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
trailing: true,
|
|
27
|
-
class: { value: "pe-10", placeholder: "pe-10" },
|
|
60
|
+
class:
|
|
61
|
+
"ring-1 ring-inset ring-border focus-visible:ring-primary focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary",
|
|
28
62
|
},
|
|
63
|
+
{ leading: true, size: "xs", class: "ps-9" },
|
|
64
|
+
{ trailing: true, size: "xs", class: "pe-9" },
|
|
65
|
+
{ leading: true, size: "sm", class: "ps-9" },
|
|
66
|
+
{ trailing: true, size: "sm", class: "pe-9" },
|
|
67
|
+
{ leading: true, size: "md", class: "ps-10.5" },
|
|
68
|
+
{ trailing: true, size: "md", class: "pe-10.5" },
|
|
69
|
+
{ leading: true, size: "lg", class: "ps-11" },
|
|
70
|
+
{ trailing: true, size: "lg", class: "pe-11" },
|
|
71
|
+
{ leading: true, size: "xl", class: "ps-12.5" },
|
|
72
|
+
{ trailing: true, size: "xl", class: "pe-12.5" },
|
|
29
73
|
],
|
|
30
74
|
defaultVariants: {
|
|
31
75
|
size: "md",
|
|
@@ -1,6 +1,78 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
slots: {
|
|
3
3
|
root: "w-full",
|
|
4
|
-
base: "w-full border-0 rounded-ml bg-surface
|
|
4
|
+
base: "w-full border-0 rounded-ml bg-surface text-text placeholder:text-dimmed transition-colors",
|
|
5
|
+
},
|
|
6
|
+
variants: {
|
|
7
|
+
size: {
|
|
8
|
+
xs: {
|
|
9
|
+
base: "px-3 py-1.5 text-xs/4 gap-2",
|
|
10
|
+
leading: "ps-3",
|
|
11
|
+
trailing: "pe-3",
|
|
12
|
+
leadingIcon: "size-icon-sm",
|
|
13
|
+
leadingAvatarSize: "3xs",
|
|
14
|
+
trailingIcon: "size-icon-sm",
|
|
15
|
+
},
|
|
16
|
+
sm: {
|
|
17
|
+
base: "px-3 py-2 text-sm/5 gap-2",
|
|
18
|
+
leading: "ps-3",
|
|
19
|
+
trailing: "pe-3",
|
|
20
|
+
leadingIcon: "size-icon-sm",
|
|
21
|
+
leadingAvatarSize: "3xs",
|
|
22
|
+
trailingIcon: "size-icon-sm",
|
|
23
|
+
},
|
|
24
|
+
md: {
|
|
25
|
+
base: "px-3.5 py-2.5 text-sm/5 gap-2",
|
|
26
|
+
leading: "ps-3.5",
|
|
27
|
+
trailing: "pe-3.5",
|
|
28
|
+
leadingIcon: "size-icon-md",
|
|
29
|
+
leadingAvatarSize: "2xs",
|
|
30
|
+
trailingIcon: "size-icon-md",
|
|
31
|
+
},
|
|
32
|
+
lg: {
|
|
33
|
+
base: "px-4 py-3 text-base/6 gap-2",
|
|
34
|
+
leading: "ps-4",
|
|
35
|
+
trailing: "pe-4",
|
|
36
|
+
leadingIcon: "size-icon-md",
|
|
37
|
+
leadingAvatarSize: "2xs",
|
|
38
|
+
trailingIcon: "size-icon-md",
|
|
39
|
+
},
|
|
40
|
+
xl: {
|
|
41
|
+
base: "px-4.5 py-3 text-lg/7 gap-2",
|
|
42
|
+
leading: "ps-4.5",
|
|
43
|
+
trailing: "pe-4.5",
|
|
44
|
+
leadingIcon: "size-icon-lg",
|
|
45
|
+
leadingAvatarSize: "xs",
|
|
46
|
+
trailingIcon: "size-icon-lg",
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
compoundVariants: [
|
|
51
|
+
{
|
|
52
|
+
color: "primary",
|
|
53
|
+
variant: ["outline", "subtle"],
|
|
54
|
+
class:
|
|
55
|
+
"ring-1 ring-inset ring-border focus-visible:ring-primary focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary",
|
|
56
|
+
},
|
|
57
|
+
{ leading: true, size: "xs", class: "ps-9" },
|
|
58
|
+
{ trailing: true, size: "xs", class: "pe-9" },
|
|
59
|
+
{ fixed: false, size: "xs", class: "max-md:text-base md:text-xs" },
|
|
60
|
+
{ leading: true, size: "sm", class: "ps-9" },
|
|
61
|
+
{ trailing: true, size: "sm", class: "pe-9" },
|
|
62
|
+
{ fixed: false, size: "sm", class: "max-md:text-base md:text-sm" },
|
|
63
|
+
{ leading: true, size: "md", class: "ps-10.5" },
|
|
64
|
+
{ trailing: true, size: "md", class: "pe-10.5" },
|
|
65
|
+
{ fixed: false, size: "md", class: "max-md:text-base md:text-sm" },
|
|
66
|
+
{ leading: true, size: "lg", class: "ps-11" },
|
|
67
|
+
{ trailing: true, size: "lg", class: "pe-11" },
|
|
68
|
+
{ fixed: false, size: "lg", class: "max-md:text-base md:text-base" },
|
|
69
|
+
{ leading: true, size: "xl", class: "ps-12.5" },
|
|
70
|
+
{ trailing: true, size: "xl", class: "pe-12.5" },
|
|
71
|
+
{ fixed: false, size: "xl", class: "max-md:text-base md:text-lg" },
|
|
72
|
+
],
|
|
73
|
+
defaultVariants: {
|
|
74
|
+
size: "md",
|
|
75
|
+
color: "primary",
|
|
76
|
+
variant: "outline",
|
|
5
77
|
},
|
|
6
78
|
};
|
|
@@ -372,6 +372,12 @@ const emit = defineEmits<{ submit: [] }>();
|
|
|
372
372
|
|
|
373
373
|
Use Tailwind utilities and theme tokens. Scoped CSS is for behavior that utilities cannot express cleanly. Dark/light modes must use the same semantic tokens; never fix one page with a hardcoded light background that breaks dark mode.
|
|
374
374
|
|
|
375
|
+
### Control Geometry and Scene Selection
|
|
376
|
+
|
|
377
|
+
**MUST** follow [DESIGN.md](../../DESIGN.md#control-geometry-and-visual-scenarios) for control sizes, text/line height, padding, radii, widths and focus treatment. Marketing CTAs and product UI share `UiButton` and the existing Nuxt UI themes. Marketing sections select prominent actions and own their wider layout; product navigation, workbenches and forms select compact or form-sized controls from the same scale.
|
|
378
|
+
|
|
379
|
+
Use `size` for the shared geometry and `block`/`fullWidth` for a full-width button. Keep section-specific CTA minimum widths at their section owner. **FORBIDDEN**: a second button implementation, marketing minimum widths in the generic theme or arbitrary per-page height/font/radius overrides for an existing size. Editable mobile fields retain the theme's readable text size; compact icon actions retain their accessible target.
|
|
380
|
+
|
|
375
381
|
### Icon Design Contract
|
|
376
382
|
|
|
377
383
|
**MUST** read [DESIGN.md](../../DESIGN.md) for icon geometry, the size scale, semantic colors and acceptance. The installed system uses local Lucide functional icons and a separate `brand` collection. `UiIcon` owns standalone `name`, `size?: string | number` and `label?: string`; Nuxt UI controls retain their internal icon slots and use the same provider.
|
|
@@ -677,7 +683,7 @@ Read [Architecture](architecture.md), [Security and Lifecycle](security-lifecycl
|
|
|
677
683
|
- [ ] Private configuration never enters public payloads.
|
|
678
684
|
- [ ] Every visible action maps to a real implementation and error path.
|
|
679
685
|
- [ ] All applicable NX-S/A/T/C/H/E/M/P/U rules are checked.
|
|
680
|
-
- [ ] Design coverage includes children, overlays, icon sizes/names/targets and loading/empty/error/forbidden states.
|
|
686
|
+
- [ ] Design coverage includes children, overlays, scene-appropriate control geometry, icon sizes/names/targets and loading/empty/error/forbidden states.
|
|
681
687
|
- [ ] `pnpm typecheck` and `pnpm build` pass for code/contract changes.
|
|
682
688
|
- [ ] Browser and network evidence proves the touched interactions.
|
|
683
689
|
- [ ] No uninstalled lint/test/authorization capability is claimed.
|
|
@@ -13,18 +13,61 @@ This document defines the visual contract for the application-owned Nuxt UI foun
|
|
|
13
13
|
|
|
14
14
|
## Ownership
|
|
15
15
|
|
|
16
|
-
| Owner
|
|
17
|
-
|
|
|
18
|
-
| `app/assets/css/site-shell.css`
|
|
19
|
-
| `app/theme/icon.ts`
|
|
20
|
-
| `app/app.config.ts`
|
|
21
|
-
| `app/components/ui/Icon.vue`
|
|
22
|
-
| `app/theme/button.ts`, `input.ts`, `checkbox.ts` | Internal icon slots and the control's geometry |
|
|
23
|
-
| `nuxt.config.ts`
|
|
24
|
-
| `app/assets/icons/brand/`
|
|
16
|
+
| Owner | Contract |
|
|
17
|
+
| ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
|
|
18
|
+
| `app/assets/css/site-shell.css` | `--site-control-height-*`, icon sizes, radius tokens, Tailwind aliases and semantic colors |
|
|
19
|
+
| `app/theme/icon.ts` | Nuxt Icon defaults: 20px token, SVG mode, non-shrinking alignment and decorative attributes |
|
|
20
|
+
| `app/app.config.ts` | Composes `icon` defaults and the Nuxt UI theme |
|
|
21
|
+
| `app/components/ui/Icon.vue` | Typed `name`, `size` and accessible `label` adapter |
|
|
22
|
+
| `app/theme/button.ts`, `input.ts`, `select.ts`, `textarea.ts`, `checkbox.ts` | Internal icon slots and the control's geometry |
|
|
23
|
+
| `nuxt.config.ts` | Locally bundled `lucide`, `brand` SVG collection and `fallbackToApi: false` |
|
|
24
|
+
| `app/assets/icons/brand/` | Reviewed third-party identity assets |
|
|
25
25
|
|
|
26
26
|
Registry-installed source belongs to this application. **MUST** change the owning token or component when a repeated pattern changes. A one-location size choice stays at its caller. **FORBIDDEN**: adding a second icon component, runtime registry, icon font or icon library for an existing Lucide capability.
|
|
27
27
|
|
|
28
|
+
## Control Geometry and Visual Scenarios
|
|
29
|
+
|
|
30
|
+
Marketing and product UI use the same `UiButton`, Nuxt UI controls and theme tokens. **MUST** choose the size and composition for the action's context; **FORBIDDEN**: separate marketing/product button implementations or campaign dimensions in the generic button default.
|
|
31
|
+
|
|
32
|
+
| Scenario | Controls | Typography and layout ownership |
|
|
33
|
+
| -------------------------------- | ----------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
|
34
|
+
| Marketing conversion | Section CTAs use `2xl` for prominence, with a section-owned minimum width of 200px and `max-w-full`. | Content sections own their larger headings, supporting copy, generous spacing and responsive content width. |
|
|
35
|
+
| Product navigation and workbench | Use `sm` for compact labeled actions and `md` for ordinary controls. Buttons fit their content. | App header, sidebar and editor owners control density, alignment and available width; controls retain the shared geometry. |
|
|
36
|
+
| Product authentication and forms | Use `lg` when the form needs a larger field and submission target. Select `block` for a full-width submit action. | The form owns field width, label/help spacing and validation space; it does not inherit a marketing CTA's minimum width. |
|
|
37
|
+
|
|
38
|
+
The control scale uses `--site-control-height-*` tokens and `h-control-*` utilities. Values below assume a 16px root; height, text and padding use rem. Text shows font size / line height. Button labels use medium weight. Input/select padding is the base padding on each side; icon slots add their own reserved space.
|
|
39
|
+
|
|
40
|
+
| Size | Button height | Input/select height | Text | Button horizontal padding | Input/select horizontal padding | Icon |
|
|
41
|
+
| -------------- | ------------- | ------------------- | --------- | ------------------------- | ------------------------------- | ---- |
|
|
42
|
+
| `xs` | 32px | 32px | 12 / 16px | 12px | 12px | 16px |
|
|
43
|
+
| `sm` | 36px | 36px | 14 / 20px | 16px | 12px | 16px |
|
|
44
|
+
| `md` (default) | 42px | 42px | 14 / 20px | 20px | 14px | 20px |
|
|
45
|
+
| `lg` | 48px | 48px | 16 / 24px | 24px | 16px | 20px |
|
|
46
|
+
| `xl` | 52px | 52px | 18 / 28px | 28px | 18px | 24px |
|
|
47
|
+
| `2xl` | 56px | — | 18 / 28px | 32px | — | 24px |
|
|
48
|
+
|
|
49
|
+
Editable inputs and textareas use 16px text below the `md` breakpoint unless the caller explicitly opts into the framework's `fixed` typography behavior. Their desktop typography follows the table. Select triggers retain their selected size. Textareas use rows/content height, not the single-line height scale; their vertical padding belongs to `app/theme/textarea.ts`.
|
|
50
|
+
|
|
51
|
+
Ordinary buttons have intrinsic width with no shared minimum or fixed width. `block` or `fullWidth` explicitly fills the available width. Marketing CTA widths belong to `content/CallToAction.vue`, `content/Faq.vue`, `content/Reviews.vue` and `steps/Showcase.vue`; page widths and heading scales remain at their layout/section owner. Link buttons retain text-flow height. Square icon buttons keep at least 44 × 44px targets even when their selected size is smaller.
|
|
52
|
+
|
|
53
|
+
Buttons, text inputs, selects and textareas use `--site-radius-md` through `rounded-ml` (10px); non-fullscreen dialogs use `--site-radius-lg` through `rounded-2ml` (20px). **MUST** reuse these radius tokens instead of adding a different radius to each field or button. Primary and neutral outline buttons and ordinary outlined fields use one 1px ring, without a second border. Primary solid buttons and primary outlined fields expose a 2px keyboard-focus outline; primary outline buttons use a 2px focus ring. Other semantic variants retain their theme-owned focus behavior. Focus treatment must remain visible and distinct from the resting edge.
|
|
54
|
+
|
|
55
|
+
Custom token utilities **MUST** be registered in `app/theme/index.ts` through `tv.twMergeConfig.extend.theme`: `radius` owns `ml`/`2ml`, and `spacing` owns the `control-*`/`icon-*` scales. This lets Nuxt UI merge framework defaults, theme values and caller overrides consistently. **FORBIDDEN**: relying on CSS source order to resolve conflicting size or radius utilities.
|
|
56
|
+
|
|
57
|
+
✅ **Select shared sizes; the caller owns emphasis and width.**
|
|
58
|
+
|
|
59
|
+
```vue
|
|
60
|
+
<UiButton size="sm" color="primary" variant="solid">Sign in</UiButton>
|
|
61
|
+
<UiButton size="lg" block type="submit">Continue</UiButton>
|
|
62
|
+
<UiButton size="2xl" class="min-w-[200px] max-w-full">Start creating</UiButton>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
❌ **A generic width mandate and per-page sizing bypass both scene owners.**
|
|
66
|
+
|
|
67
|
+
```vue
|
|
68
|
+
<UiButton class="h-[39px] min-w-[200px] rounded-[7px] text-[15px]">Save</UiButton>
|
|
69
|
+
```
|
|
70
|
+
|
|
28
71
|
## Functional Icon Geometry
|
|
29
72
|
|
|
30
73
|
Functional icons **MUST** use `i-lucide-*` names from the installed collection. Their design uses a square 24 × 24 viewBox, 2-unit centered strokes, rounded open-path caps and rounded joins. Preserve the family's edge clearance, internal spacing and visual balance; identical CSS dimensions alone do not establish equal optical weight. These constraints follow the [Lucide specification](https://lucide.dev/contribute/icons/specification).
|
|
@@ -136,6 +179,8 @@ Use the existing locale owner for labels, visible focus on controls and the comp
|
|
|
136
179
|
|
|
137
180
|
### Acceptance Checklist
|
|
138
181
|
|
|
182
|
+
- [ ] Marketing CTAs and product actions use the same controls with the appropriate size and caller-owned width.
|
|
183
|
+
- [ ] Actual control heights, typography, padding, radii, border/focus states and narrow-view input text match the shared contract.
|
|
139
184
|
- [ ] Functional icons come from the installed Lucide family; brand assets remain separate.
|
|
140
185
|
- [ ] The 20px default, selected size tokens and explicit `size` override render correctly without competing size declarations.
|
|
141
186
|
- [ ] Source geometry and proportional strokes are preserved; no arbitrary per-icon offsets or global SVG overrides exist.
|