Skip to content

Commit 406d19d

Browse files
feat: added shadcn v4 components and improved tailwind.css
1 parent 383de65 commit 406d19d

File tree

17 files changed

+802
-17
lines changed

17 files changed

+802
-17
lines changed

app/components/forms.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { useInputControl } from '@conform-to/react'
22
import { REGEXP_ONLY_DIGITS_AND_CHARS, type OTPInputProps } from 'input-otp'
33
import React, { useId } from 'react'
4-
import { Checkbox, type CheckboxProps } from './ui/checkbox.tsx'
4+
5+
import { Checkbox, type CheckboxProps } from '#app/components/ui/checkbox.tsx'
56
import {
67
InputOTP,
78
InputOTPGroup,
@@ -26,7 +27,7 @@ export function ErrorList({
2627
return (
2728
<ul id={id} className="flex flex-col gap-1">
2829
{errorsToRender.map((e) => (
29-
<li key={e} className="text-[10px] text-foreground-destructive">
30+
<li key={e} className="text-foreground-destructive text-[10px]">
3031
{e}
3132
</li>
3233
))}
@@ -57,7 +58,7 @@ export function Field({
5758
aria-describedby={errorId}
5859
{...inputProps}
5960
/>
60-
<div className="min-h-[32px] px-4 pb-3 pt-1">
61+
<div className="min-h-[32px] px-4 pt-1 pb-3">
6162
{errorId ? <ErrorList id={errorId} errors={errors} /> : null}
6263
</div>
6364
</div>
@@ -101,7 +102,7 @@ export function OTPField({
101102
<InputOTPSlot index={5} />
102103
</InputOTPGroup>
103104
</InputOTP>
104-
<div className="min-h-[32px] px-4 pb-3 pt-1">
105+
<div className="min-h-[32px] px-4 pt-1 pb-3">
105106
{errorId ? <ErrorList id={errorId} errors={errors} /> : null}
106107
</div>
107108
</div>
@@ -131,7 +132,7 @@ export function TextareaField({
131132
aria-describedby={errorId}
132133
{...textareaProps}
133134
/>
134-
<div className="min-h-[32px] px-4 pb-3 pt-1">
135+
<div className="min-h-[32px] px-4 pt-1 pb-3">
135136
{errorId ? <ErrorList id={errorId} errors={errors} /> : null}
136137
</div>
137138
</div>
@@ -191,10 +192,10 @@ export function CheckboxField({
191192
<label
192193
htmlFor={id}
193194
{...labelProps}
194-
className="self-center text-body-xs text-muted-foreground"
195+
className="text-body-xs text-muted-foreground self-center"
195196
/>
196197
</div>
197-
<div className="px-4 pb-3 pt-1">
198+
<div className="px-4 pt-1 pb-3">
198199
{errorId ? <ErrorList id={errorId} errors={errors} /> : null}
199200
</div>
200201
</div>

app/components/progress-bar.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@ function EpicProgress() {
3232
role="progressbar"
3333
aria-hidden={delayedPending ? undefined : true}
3434
aria-valuetext={delayedPending ? 'Loading' : undefined}
35-
className="fixed inset-x-0 left-0 top-0 z-50 h-[0.20rem] animate-pulse"
35+
className="fixed inset-x-0 top-0 left-0 z-50 h-[0.20rem] animate-pulse"
3636
>
3737
<div
3838
ref={ref}
3939
className={cn(
40-
'h-full w-0 bg-foreground duration-500 ease-in-out',
40+
'bg-foreground h-full w-0 duration-500 ease-in-out',
4141
transition.state === 'idle' &&
4242
(animationComplete
4343
? 'transition-none'
@@ -51,7 +51,7 @@ function EpicProgress() {
5151
<Icon
5252
name="update"
5353
size="md"
54-
className="m-1 animate-spin text-foreground"
54+
className="text-foreground m-1 animate-spin"
5555
aria-hidden
5656
/>
5757
</div>

app/components/ui/button.tsx

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { Slot } from '@radix-ui/react-slot'
2+
import { cva, type VariantProps } from 'class-variance-authority'
3+
import * as React from 'react'
4+
import { cn } from '#app/utils/misc.tsx'
5+
6+
const buttonVariants = cva(
7+
"focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
8+
{
9+
variants: {
10+
variant: {
11+
default:
12+
'bg-primary text-primary-foreground hover:bg-primary/90 shadow-xs',
13+
destructive:
14+
'bg-destructive hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 text-white shadow-xs',
15+
outline:
16+
'border-input bg-background hover:bg-accent hover:text-accent-foreground border shadow-xs',
17+
secondary:
18+
'bg-secondary text-secondary-foreground hover:bg-secondary/80 shadow-xs',
19+
ghost: 'hover:bg-accent hover:text-accent-foreground',
20+
link: 'text-primary underline-offset-4 hover:underline',
21+
},
22+
size: {
23+
default: 'h-10 px-4 py-2 has-[>svg]:px-3',
24+
wide: 'px-24 py-5',
25+
sm: 'h-9 rounded-md px-3 has-[>svg]:px-2.5',
26+
lg: 'h-11 rounded-md px-6 has-[>svg]:px-4',
27+
pill: 'px-12 py-3 leading-3',
28+
icon: 'size-10',
29+
},
30+
},
31+
defaultVariants: {
32+
variant: 'default',
33+
size: 'default',
34+
},
35+
},
36+
)
37+
38+
export interface ButtonProps
39+
extends React.ComponentProps<'button'>,
40+
VariantProps<typeof buttonVariants> {
41+
asChild?: boolean
42+
}
43+
44+
function Button({
45+
className,
46+
variant,
47+
size,
48+
asChild = false,
49+
...props
50+
}: ButtonProps) {
51+
const Comp = asChild ? Slot : 'button'
52+
53+
return (
54+
<Comp
55+
data-slot="button"
56+
className={cn(buttonVariants({ variant, size, className }))}
57+
{...props}
58+
/>
59+
)
60+
}
61+
62+
export { Button, buttonVariants }

app/components/ui/checkbox.tsx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import * as CheckboxPrimitive from '@radix-ui/react-checkbox'
2+
import * as React from 'react'
3+
4+
import { cn } from '#app/utils/misc.tsx'
5+
6+
export type CheckboxProps = Omit<
7+
React.ComponentProps<typeof CheckboxPrimitive.Root>,
8+
'type'
9+
> & {
10+
type?: string
11+
}
12+
13+
function Checkbox({
14+
className,
15+
...props
16+
}: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
17+
return (
18+
<CheckboxPrimitive.Root
19+
data-slot="checkbox"
20+
className={cn(
21+
'peer border-input data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',
22+
className,
23+
)}
24+
{...props}
25+
>
26+
<CheckboxPrimitive.Indicator
27+
data-slot="checkbox-indicator"
28+
className={cn('flex items-center justify-center text-current')}
29+
>
30+
<svg viewBox="0 0 8 8">
31+
<path
32+
d="M1,4 L3,6 L7,2"
33+
stroke="currentcolor"
34+
strokeWidth="1"
35+
fill="none"
36+
/>
37+
</svg>
38+
</CheckboxPrimitive.Indicator>
39+
</CheckboxPrimitive.Root>
40+
)
41+
}
42+
43+
export { Checkbox }

0 commit comments

Comments
 (0)