Search code examples
next.jstailwind-cssshadcnui

Tailwind styles not applied to components inside Shadcn Sheet


While 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. For example, when setting a background color to a Button in a Form inside the Sheet component, the background of the Button becomes completely transparent/white.

Basically any Tailwind style is ignored inside the Sheet. Interesting behaviour is, for example, if you set the bg-color of the button to a color, for example bg-yellow-300, the button will lose any color and become "hidden", but if outside this Sheet you use that same color, the background color will be applied correctly to the button. Very strange behaviour, is like inside the Sheet that Tailwind property is not compiled (?).

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 {insertEntrySchema} from "@/db/schema";
import {z} from "zod";
import {useNewEntry} from "@/features/entry/hooks/use-new-entry";
import {useCreateEntry} from "@/features/entry/api/use-create-entry";
import {Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle} from "@/components/ui/sheet";
import {EntryForm} from "@/features/entry/components/entry-form";


const formSchema = insertEntrySchema.omit({
    userId: true
})
type FormValues = z.input<typeof formSchema>
export const NewEntrySheet = () => {
    const { isOpen, onClose } = useNewEntry()
    const mutation = useCreateEntry()

    const onSubmit = (values: FormValues) => {
        mutation.mutate(values, {
            onSuccess: () => {
                onClose();
            }
        })
    }

    return (
        <Sheet open={isOpen} onOpenChange={onClose}>
            <SheetContent className="space-y-4">
                <SheetHeader>
                    <SheetTitle>
                        New Data Set
                    </SheetTitle>
                    <SheetDescription className={"text-yellow-600"}>
                        Create a new data set to track your data and manage your data.
                    </SheetDescription>
                </SheetHeader>
                <EntryForm onSubmit={onSubmit} disabled={mutation.isPending}  />
            </SheetContent>
        </Sheet>
    )

}

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.


Solution

  • Found the solution, and it was a very stpd miss configuration.

    I divided the functionality of the app in a folder called features. As you can imagine, the features path was not included in the content array of the tailwind.config.ts.

    adding the features path to the content of the tailwind.config.ts file solved the issue.

    This is how it looks now:

    content: [
        './pages/**/*.{ts,tsx}',
        './components/**/*.{ts,tsx}',
        './app/**/*.{ts,tsx}',
        './features/**/*.{js,ts,jsx,tsx}'
      ]