Building a project in Nextjs 14 using tailwind and Shadcn
components I ran into an awkward issue, particularly with the Sheet
component from Shadcn, making it impossible to style components inside of it. E.g. setting a background color to a Button
in a Form
inside the Sheet
component, the background of the Button
becomes completely transparent/white.
Same happens if you attempt to change the text color of a Button
, in this case becomes black. Basically any tailwind style is ignored inside the Sheet
.
Interesting behaviour is, if you set the bg color of the button
to a color, for example bg-yellow-300
, the button
will lose any color and becomes “hidden”, but if you outside this Sheet
, use that color, the background color will be applied correctly to the button
. Very strange behaviour.
Code of the Form
entry-form.tsx
import {useFieldArray, useForm} from "react-hook-form";
import {zodResolver} from "@hookform/resolvers/zod";
import {z} from "zod";
import {Form, FormControl, FormField, FormItem, FormLabel} from "@/components/ui/form";
import {Input} from "@/components/ui/input";
import {Button} from "@/components/ui/button";
import {Select} from "@/components/select";
import {Plus} from "lucide-react";
const dynamicFieldSchema = z.object({
name: z.string(),
type: z.string(),
})
const propertiesSchema = z.array(dynamicFieldSchema)
const formSchema = z.object({
name: z.string(),
url: z.string(),
properties: propertiesSchema
})
type FormValues = z.input<typeof formSchema>
type Props = {
id?: string;
defaultValues?: FormValues;
onSubmit: (values: FormValues) => void;
onDelete?: () => void;
disabled?: boolean;
}
export const EntryForm = (
{
id,
defaultValues,
onSubmit,
onDelete,
disabled
}: Props
) => {
const form = useForm<FormValues>({
resolver: zodResolver(formSchema),
defaultValues: {
name: "",
url: "",
properties: []
}
})
const {control, handleSubmit} = form
const {fields, append, remove} = useFieldArray({
control,
name: "properties"
})
const typeOptions = [
{label: "STRING", value: "string"},
{label: "NUMBER", value: "number"},
{label: "BOOLEAN", value: "boolean"},
{label: "DOUBLE", value: "double"},
]
const submit = (values: FormValues) => {
console.log({values})
onSubmit(values)
}
const handleDelete = () => {
onDelete?.()
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(submit)} className="space-y-4">
<FormField
name={"name"}
control={form.control}
render={({field}) => (
<FormItem>
<FormLabel>
Name of set
</FormLabel>
<FormControl>
<Input
disabled={disabled}
placeholder={"Posts..."}
{...field}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
name={"url"}
control={form.control}
render={({field}) => (
<FormItem>
<FormLabel>
URL of set
</FormLabel>
<FormControl>
<Input
disabled={disabled}
placeholder={"https://myweb.com/user?=1/posts..."}
{...field}
/>
</FormControl>
</FormItem>
)}
/>
{fields.map((item, index) => (
<div key={item.id} className="flex flex-col space-y-4">
<div className="flex flex-row gap-x-2">
<FormField
name={`properties.${index}.name`}
control={form.control}
render={({field}) => (
<FormItem>
<FormLabel>
Name
</FormLabel>
<FormControl>
<Input
disabled={disabled}
placeholder={"name"}
{...field}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
name={`properties.${index}.type`}
control={form.control}
render={({field}) => (
<FormItem>
<FormLabel>
Type
</FormLabel>
<FormControl>
<Select
placeholder={"STRING"}
options={typeOptions}
disabled={disabled}
onCreate={(name) => {
console.log("Creating", name)
}}
value={field.value}
onChange={field.onChange}
/>
</FormControl>
</FormItem>
)}
/>
</div>
<Button
type={"button"}
onClick={() => remove(index)}
>
Remove property
</Button>
</div>
))}
{/* this className is not applied, not a single attribute */}
<div className="flex flex-col space-y-4 border-2 border-sky-700 rounded-md">
<p className="text-sm font-medium">
Define your object response type
</p>
{/* this className is not applied, no way to set any styling to the button*/}
<Button
type={"button"}
onClick={() => append({name: "", type: ""})}
className="w-fit bg-zinc-600"
>
<Plus className="size-4 mr-2"/>
Add property
</Button>
</div>
<Button className="w-full" disabled={disabled}>
{id ? "Save changes" : "Create set"}
</Button>
</form>
</Form>
)
}
Code of the Sheet
new-entry-sheet.tsx
import {useFieldArray, useForm} from "react-hook-form";
import {zodResolver} from "@hookform/resolvers/zod";
import {z} from "zod";
import {Form, FormControl, FormField, FormItem, FormLabel} from "@/components/ui/form";
import {Input} from "@/components/ui/input";
import {Button} from "@/components/ui/button";
import {Select} from "@/components/select";
import {Plus} from "lucide-react";
const dynamicFieldSchema = z.object({
name: z.string(),
type: z.string(),
})
const propertiesSchema = z.array(dynamicFieldSchema)
const formSchema = z.object({
name: z.string(),
url: z.string(),
properties: propertiesSchema
})
type FormValues = z.input<typeof formSchema>
type Props = {
id?: string;
defaultValues?: FormValues;
onSubmit: (values: FormValues) => void;
onDelete?: () => void;
disabled?: boolean;
}
export const EntryForm = (
{
id,
defaultValues,
onSubmit,
onDelete,
disabled
}: Props
) => {
const form = useForm<FormValues>({
resolver: zodResolver(formSchema),
defaultValues: {
name: "",
url: "",
properties: []
}
})
const {control, handleSubmit} = form
const {fields, append, remove} = useFieldArray({
control,
name: "properties"
})
const typeOptions = [
{label: "STRING", value: "string"},
{label: "NUMBER", value: "number"},
{label: "BOOLEAN", value: "boolean"},
{label: "DOUBLE", value: "double"},
]
const submit = (values: FormValues) => {
console.log({values})
onSubmit(values)
}
const handleDelete = () => {
onDelete?.()
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(submit)} className="space-y-4">
<FormField
name={"name"}
control={form.control}
render={({field}) => (
<FormItem>
<FormLabel>
Name of set
</FormLabel>
<FormControl>
<Input
disabled={disabled}
placeholder={"Posts..."}
{...field}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
name={"url"}
control={form.control}
render={({field}) => (
<FormItem>
<FormLabel>
URL of set
</FormLabel>
<FormControl>
<Input
disabled={disabled}
placeholder={"https://myweb.com/user?=1/posts..."}
{...field}
/>
</FormControl>
</FormItem>
)}
/>
{fields.map((item, index) => (
<div key={item.id} className="flex flex-col space-y-4">
<div className="flex flex-row gap-x-2">
<FormField
name={`properties.${index}.name`}
control={form.control}
render={({field}) => (
<FormItem>
<FormLabel>
Name
</FormLabel>
<FormControl>
<Input
disabled={disabled}
placeholder={"name"}
{...field}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
name={`properties.${index}.type`}
control={form.control}
render={({field}) => (
<FormItem>
<FormLabel>
Type
</FormLabel>
<FormControl>
<Select
placeholder={"STRING"}
options={typeOptions}
disabled={disabled}
onCreate={(name) => {
console.log("Creating", name)
}}
value={field.value}
onChange={field.onChange}
/>
</FormControl>
</FormItem>
)}
/>
</div>
<Button
type={"button"}
onClick={() => remove(index)}
>
Remove property
</Button>
</div>
))}
<div className="flex flex-col space-y-4 border-2 border-sky-700 rounded-md">
<p className="text-sm font-medium">
Define your object response type
</p>
<Button
type={"button"}
onClick={() => append({name: "", type: ""})}
className="w-fit bg-zinc-600"
>
<Plus className="size-4 mr-2"/>
Add property
</Button>
</div>
<Button className="w-full" disabled={disabled}>
{id ? "Save changes" : "Create set"}
</Button>
</form>
</Form>
)
}
Tailwind is working correctly in the rest of the project, and in the rest of the shadcn components, styles can be applied to buttons outside of the Sheet
.
I have tried:
- Checking if my
tailwind.config.ts
is correct. - Checking if my
components.json
is correct. - Checking if my
globals.css
is correct.
Any help is appreciated, let me know what else should I attach. Thanks in advance.