Skip to content

Instantly share code, notes, and snippets.

@nimone
Created July 6, 2023 07:31
Show Gist options
  • Save nimone/373fc7d2184a1534c2a91bc4b52ff235 to your computer and use it in GitHub Desktop.
Save nimone/373fc7d2184a1534c2a91bc4b52ff235 to your computer and use it in GitHub Desktop.
Build a Plan Selection Page with Custom Radio Component using React and Tailwind CSS
import { useState } from "react"
import Radio, { RadioGroup } from "./components/Radio"
import { BadgePercent, Sparkle, Gem, Crown, ArrowRight } from "lucide-react"
export default function App() {
const [plan, setPlan] = useState("")
return (
<main className="min-h-screen flex flex-col items-center justify-center">
<h2 className="text-2xl font-bold tracking-tight">Choose Your Plan</h2>
<hr className="my-3 w-56" />
<RadioGroup value={plan} onChange={(e) => setPlan(e.target.value)}>
<div className="flex gap-4 justify-center flex-col">
<Radio value="free">
<Plan
icon={<BadgePercent />}
title="Free"
features={["SD (480p)", "Mobile", "Ads"]}
price={0}
/>
</Radio>
<Radio value="basic">
<Plan
icon={<Sparkle />}
title="Basic"
features={["HD (720p)", "1 Device"]}
price={4.99}
/>
</Radio>
<Radio value="standard">
<Plan
icon={<Gem />}
title="Standard"
features={["Full HD (1080p)", "2 Devices"]}
price={9.99}
/>
</Radio>
<Radio value="premium">
<Plan
icon={<Crown />}
title="Premium"
features={["Ultra HD (4K) + HDR", "4 Devices"]}
price={14.99}
/>
</Radio>
</div>
</RadioGroup>
<hr className="my-3 w-56" />
<button
className={`
flex gap-4 items-center px-6 py-3 rounded-lg
bg-violet-800 hover:bg-violet-700
font-semibold text-lg text-white
`}
>
Proceed with {plan} plan
<ArrowRight />
</button>
</main>
)
}
function Plan({ icon, title, features, price }) {
return (
<div className="flex gap-4 items-center">
{icon}
<div>
<h3 className="text-lg font-semibold">{title}</h3>
<p className="text-sm">{features.join(" · ")}</p>
</div>
<span className="ml-auto font-medium">${price}</span>
</div>
)
}
import { useContext, createContext } from "react"
const RadioContext = createContext()
export default function Radio({ children, ...props }) {
const { value, onChange } = useContext(RadioContext)
return (
<label
className={`
px-6 py-4 shadow rounded-lg cursor-pointer
transition-all ${
value === props.value
? "bg-gradient-to-t from-violet-200 to-violet-100 text-violet-800 shadow-violet-500 scale-105"
: "bg-white hover:shadow-md shadow-gray-300"
}
`}
>
<input
type="radio"
className="hidden"
checked={value === props.value}
onChange={onChange}
{...props}
/>
{children}
</label>
)
}
export function RadioGroup({ value, onChange, children }) {
return (
<RadioContext.Provider value={{ value, onChange }}>
{children}
</RadioContext.Provider>
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment