Created
July 6, 2023 07:31
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | |
) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
Thank you! Helped me very much!