@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 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.6.0");
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: 10px;
40
- --radius-2ml: 20px;
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="lg">{{ buttonLabel }}</UiButton></slot
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="lg"
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
  >
@@ -51,8 +51,8 @@
51
51
 
52
52
  <UiButton
53
53
  v-if="showButton"
54
- size="lg"
55
- class="flex justify-center mx-auto"
54
+ size="2xl"
55
+ class="w-fit min-w-[200px] max-w-full flex justify-center mx-auto"
56
56
  :label="buttonText"
57
57
  ripple
58
58
  :to="to"
@@ -144,9 +144,9 @@ onMounted(resumeAutoplay);
144
144
  <slot name="footer">
145
145
  <UiButton
146
146
  v-if="showButton"
147
- size="lg"
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 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 min-w-[200px]",
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-1 text-xs h-8 gap-1",
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-1.5 text-sm h-10 gap-1.5",
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-2 text-base h-12 gap-2",
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-2.5 text-sm w-[214px] h-[54px] gap-2",
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-3 text-xl w-[240px] h-16 gap-2",
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-3.5 font-semibold text-lg w-[310px] h-15 gap-2 max-lg:w-[285px] max-lg:h-[48px]",
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-none",
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-15 min-w-0 p-3.5 max-lg:w-12" },
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-[2px] 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",
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-[2px] 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",
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-8 min-w-0 p-1",
153
+ class: "w-control-xs p-0",
159
154
  },
160
155
  {
161
156
  size: "sm",
162
157
  square: true,
163
- class: "w-10 min-w-0 p-1.5",
158
+ class: "w-control-sm p-0",
164
159
  },
165
160
  {
166
161
  size: "md",
167
162
  square: true,
168
- class: "w-12 min-w-0 p-1.5",
163
+ class: "w-control-md p-0",
169
164
  },
170
165
  {
171
166
  size: "lg",
172
167
  square: true,
173
- class: "w-[54px] min-w-0 p-2",
168
+ class: "w-control-lg p-0",
174
169
  },
175
170
  {
176
171
  size: "xl",
177
172
  square: true,
178
- class: "w-16 min-w-0 p-2",
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: "success",
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: ["min-h-11 border-0 rounded-ml bg-surface text-text", "transition-colors"],
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-2 py-1 text-xs gap-1",
10
- leading: "ps-2",
11
- trailing: "pe-2",
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-2.5 py-1.5 text-xs gap-1.5",
18
- leading: "ps-2.5",
19
- trailing: "pe-2.5",
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-2.5 py-1.5 text-sm gap-1.5",
26
- leading: "ps-2.5",
27
- trailing: "pe-2.5",
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-3 py-2 text-sm gap-2",
34
- leading: "ps-3",
35
- trailing: "pe-3",
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-3 py-2 text-base gap-2",
42
- leading: "ps-3",
43
- trailing: "pe-3",
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-border text-base placeholder:text-dimmed focus-visible:ring-1! focus-visible:outline-none max-md:text-sm",
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",
@@ -8,6 +8,6 @@ export default {
8
8
  },
9
9
  variants: {
10
10
  overlay: { true: { overlay: "bg-neutral/60" } },
11
- fullscreen: { false: { content: "rounded-ml ring-0" } },
11
+ fullscreen: { false: { content: "rounded-2ml ring-0" } },
12
12
  },
13
13
  };
@@ -1,31 +1,75 @@
1
1
  export default {
2
2
  slots: {
3
- base: ["min-h-11 border-0 rounded-ml bg-surface text-text"],
4
- value: "ps-5 text-base max-md:text-sm",
5
- placeholder: "text-dimmed ps-5 text-base max-md:text-sm",
6
- group: "p-2.5",
7
- content: "border-0 rounded-ml ring-1 ring-border bg-surface-muted",
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
- itemTrailingIcon: "hidden",
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: "p-0 ring-border focus-visible:ring-1! focus-visible:outline-none",
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 px-3 py-2 text-sm leading-6 text-text placeholder:text-dimmed ring-border focus-visible:ring-1! focus-visible:ring-primary focus-visible:outline-none",
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
  };
@@ -21,7 +21,7 @@
21
21
  "name": "@stackonward/cli",
22
22
  "description": "StackOnward project and resource CLI",
23
23
  "type": "npm",
24
- "version": "^1.6.0"
24
+ "version": "^1.7.0"
25
25
  },
26
26
  "@stackonward/cms-client": {
27
27
  "name": "@stackonward/cms-client",
@@ -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 | Contract |
17
- | ------------------------------------------------ | ------------------------------------------------------------------------------------------- |
18
- | `app/assets/css/site-shell.css` | `--site-icon-*` size tokens, `size-icon-*` 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`, `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 |
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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackonward/cli",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "description": "StackOnward CLI for creating projects and installing reusable components and snippets",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",