Skip to content

Commit 502efdb

Browse files
committed
feat(Button): Allow custom variants with type safety
1 parent 1f789f0 commit 502efdb

File tree

5 files changed

+62
-59
lines changed

5 files changed

+62
-59
lines changed

docs/app.config.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,22 @@ export default defineAppConfig({
55
container: {
66
constrained: "max-w-2xl",
77
},
8+
9+
button: {
10+
variant: {
11+
"primary-solid":
12+
"bg-primary-500 text-white shadow-sm hover:bg-primary-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-500 disabled:bg-primary-500",
13+
"primary-soft":
14+
"bg-primary-50 text-primary-600 hover:bg-primary-100 focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-500 disabled:bg-primary-50",
15+
"primary-ghost":
16+
"text-primary-500 hover:bg-primary-50 focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-500 disabled:bg-transparent",
17+
"primary-link":
18+
"text-primary-500 underline-offset-4 hover:text-primary-600 hover:underline focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-500 disabled:text-primary-500",
19+
},
20+
21+
default: {
22+
variant: "primary-solid",
23+
},
24+
},
825
},
926
});

docs/pages/button.vue

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,31 +12,31 @@ import { UiButton, UiContainer } from "#components";
1212

1313
<div class="mt-2 flex items-center gap-4">
1414
<UiButton label="Solid" />
15-
<UiButton label="Soft" variant="soft" />
16-
<UiButton label="Ghost" variant="ghost" />
17-
<UiButton label="Link" variant="link" />
15+
<UiButton label="Soft" variant="primary-soft" />
16+
<UiButton label="Ghost" variant="primary-ghost" />
17+
<UiButton label="Link" variant="primary-link" />
1818
</div>
1919
</div>
2020

2121
<div class="demo-category-container mt-4">
2222
<span class="demo-category-title">White</span>
2323

2424
<div class="mt-2 flex items-center p-4 bg-gray-900 rounded-md gap-4">
25-
<UiButton label="Solid" color="white" />
26-
<UiButton label="Soft" color="white" variant="soft" />
27-
<UiButton label="Ghost" color="white" variant="ghost" />
28-
<UiButton label="Link" color="white" variant="link" />
25+
<UiButton label="Solid" variant="white-solid" />
26+
<UiButton label="Soft" variant="white-soft" />
27+
<UiButton label="Ghost" variant="white-ghost" />
28+
<UiButton label="Link" variant="white-link" />
2929
</div>
3030
</div>
3131

3232
<div class="demo-category-container mt-4">
3333
<span class="demo-category-title">Black</span>
3434

3535
<div class="mt-2 flex items-center gap-4">
36-
<UiButton label="Solid" color="black" />
37-
<UiButton label="Soft" color="black" variant="soft" />
38-
<UiButton label="Ghost" color="black" variant="ghost" />
39-
<UiButton label="Link" color="black" variant="link" />
36+
<UiButton label="Solid" variant="black-solid" />
37+
<UiButton label="Soft" variant="black-soft" />
38+
<UiButton label="Ghost" variant="black-ghost" />
39+
<UiButton label="Link" variant="black-link" />
4040
</div>
4141
</div>
4242

@@ -49,11 +49,11 @@ import { UiButton, UiContainer } from "#components";
4949
leading-icon="i-heroicons-lock-closed-20-solid"
5050
/>
5151
<UiButton
52-
color="white"
5352
label="Trailing"
53+
variant="white-solid"
5454
leading-icon="i-heroicons-lock-closed-20-solid"
5555
/>
56-
<UiButton loading color="black" label="Loading" />
56+
<UiButton loading variant="black-solid" label="Loading" />
5757
</div>
5858
</div>
5959

@@ -62,8 +62,8 @@ import { UiButton, UiContainer } from "#components";
6262

6363
<div class="mt-2 flex items-center gap-4">
6464
<UiButton disabled label="Primary" />
65-
<UiButton disabled label="White" color="white" />
66-
<UiButton disabled label="Black" color="black" />
65+
<UiButton disabled label="White" variant="white-solid" />
66+
<UiButton disabled label="Black" variant="black-solid" />
6767
</div>
6868
</div>
6969

src/runtime/components/elements/Button.vue

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,7 @@ import { twJoin, twMerge } from "tailwind-merge";
33
import type { PropType } from "vue";
44
import { computed, defineComponent, toRef } from "vue";
55
import { useUI } from "../../composables/useUI";
6-
import type {
7-
Button,
8-
ButtonColor,
9-
ButtonSize,
10-
ButtonVariant,
11-
Strategy,
12-
} from "../../types";
6+
import type { Button, ButtonSize, ButtonVariant, Strategy } from "../../types";
137
import { button } from "../../ui.config";
148
import { mergeConfig } from "../../utils";
159
import UiIcon from "../elements/Icon.vue";
@@ -46,10 +40,6 @@ export default defineComponent<Button>({
4640
type: String as PropType<ButtonSize>,
4741
default: () => config.default.size,
4842
},
49-
color: {
50-
type: String as PropType<ButtonColor>,
51-
default: () => config.default.color,
52-
},
5343
variant: {
5444
type: String as PropType<ButtonVariant>,
5545
default: () => config.default.variant,
@@ -83,7 +73,7 @@ export default defineComponent<Button>({
8373
ui.value.size[props.size!],
8474
ui.value.gap[props.size!],
8575
props.padded && ui.value.padding[props.size!],
86-
ui.value.color[props.color!][props.variant!],
76+
ui.value.variant[props.variant!],
8777
props.block
8878
? "w-full flex justify-center items-center"
8979
: "inline-flex items-center",

src/runtime/types/button.d.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1+
import type { AppConfig } from "nuxt/schema";
12
import { button } from "../ui.config";
23
import type { Link } from "./link";
3-
import type { DeepPartial, Strategy } from "./utils";
4+
import type { DeepPartial, ExtractDeepKey, Strategy } from "./utils";
45

5-
export type ButtonSize = keyof typeof button.size;
6-
export type ButtonColor = "primary" | "white" | "black";
7-
export type ButtonVariant = "solid" | "soft" | "ghost" | "link";
6+
export type ButtonSize =
7+
| keyof typeof button.size
8+
| ExtractDeepKey<AppConfig, ["ui", "button", "size"]>;
9+
10+
export type ButtonVariant =
11+
| keyof typeof button.variant
12+
| ExtractDeepKey<AppConfig, ["ui", "button", "variant"]>;
813

914
export interface Button extends Link {
1015
label?: string;
@@ -18,7 +23,6 @@ export interface Button extends Link {
1823
block?: boolean;
1924
padded?: boolean;
2025
size?: ButtonSize;
21-
color?: ButtonColor;
2226
variant?: ButtonVariant;
2327
ui?: DeepPartial<typeof button & { strategy?: Strategy }>;
2428
class?: any;

src/runtime/ui.config.ts

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -135,31 +135,24 @@ export const button = {
135135
sm: "px-4 py-2",
136136
md: "px-5 py-2.5",
137137
},
138-
color: {
139-
primary: {
140-
solid:
141-
"bg-primary-500 text-white shadow-sm hover:bg-primary-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-500 disabled:bg-primary-500",
142-
soft: "bg-primary-50 text-primary-600 hover:bg-primary-100 focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-500 disabled:bg-primary-50",
143-
ghost:
144-
"text-primary-500 hover:bg-primary-50 focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-500 disabled:bg-transparent",
145-
link: "text-primary-500 underline-offset-4 hover:text-primary-600 hover:underline focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-500 disabled:text-primary-500",
146-
},
147-
white: {
148-
solid:
149-
"bg-white text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-400 disabled:bg-white",
150-
soft: "bg-white/20 text-white hover:bg-white/30 focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-500 disabled:bg-white/10",
151-
ghost:
152-
"text-white hover:bg-white/30 focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-500 disabled:bg-transparent",
153-
link: "text-white underline-offset-4 hover:underline focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-400 disabled:text-white",
154-
},
155-
black: {
156-
solid:
157-
"bg-gray-900 text-white shadow-sm hover:bg-gray-800 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-400 disabled:bg-gray-900",
158-
soft: "bg-gray-100 text-gray-700 hover:bg-gray-200 focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-500 disabled:bg-gray-50",
159-
ghost:
160-
"text-gray-700 hover:bg-gray-200 hover:text-gray-900 focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-500 disabled:bg-transparent",
161-
link: "text-gray-900 underline-offset-4 hover:underline focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-500 disabled:text-gray-900",
162-
},
138+
variant: {
139+
"white-solid":
140+
"bg-white text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-400 disabled:bg-white",
141+
"white-soft":
142+
"bg-white/20 text-white hover:bg-white/30 focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-500 disabled:bg-white/10",
143+
"white-ghost":
144+
"text-white hover:bg-white/30 focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-500 disabled:bg-transparent",
145+
"white-link":
146+
"text-white underline-offset-4 hover:underline focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-400 disabled:text-white",
147+
148+
"black-solid":
149+
"bg-gray-900 text-white shadow-sm hover:bg-gray-800 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-400 disabled:bg-gray-900",
150+
"black-soft":
151+
"bg-gray-100 text-gray-700 hover:bg-gray-200 focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-500 disabled:bg-gray-50",
152+
"black-ghost":
153+
"text-gray-700 hover:bg-gray-200 hover:text-gray-900 focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-500 disabled:bg-transparent",
154+
"black-link":
155+
"text-gray-900 underline-offset-4 hover:underline focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-500 disabled:text-gray-900",
163156
},
164157
icon: {
165158
base: "flex-shrink-0",
@@ -171,8 +164,7 @@ export const button = {
171164
},
172165
default: {
173166
size: "sm",
174-
color: "primary",
175-
variant: "solid",
167+
variant: "black-solid",
176168
loadingIcon: "i-heroicons-arrow-path-20-solid",
177169
},
178170
};

0 commit comments

Comments
 (0)