Features
Responsive feature grid. Icon, title, and description per card. Define features once in the array, the layout adapts.
What it is
A responsive grid of feature cards. Each card has a Lucide icon, a title, and a short description. The grid is 1 column on mobile, 2 columns on tablet, 3 columns on desktop. The features themselves are defined as an array in components/landing/Features.tsx, so adding or removing items requires editing one array, not the layout.
Usage
import { Features } from '@/components/landing/Features'
export default function Page() {
return <Features />
}
Customization
Edit components/landing/Features.tsx. The features array is the only thing to change for content:
import { Shield, Lock, Zap } from 'lucide-react'
const features = [
{
icon: Shield,
title: 'RLS deny-all by default',
description:
'Every table has Row Level Security enabled with no policies. The anon key reads nothing without explicit opt-in.',
},
{
icon: Lock,
title: 'Zod on every Server Action',
description:
'All user input is validated against typed schemas before reaching the database. No unvalidated mutation runs.',
},
// Add or remove features...
]
Icons
Import any icon from lucide-react. Icon components are tree-shakeable, so unused ones do not ship in the bundle.
import { Shield, Zap, CreditCard, Database } from 'lucide-react'
Grid columns
Default is 1 / 2 / 3 (mobile / tablet / desktop). Change the Tailwind classes:
<div className="grid gap-8 md:grid-cols-2 lg:grid-cols-3">
For a 2-column desktop layout, swap lg:grid-cols-3 for lg:grid-cols-2. For a 4-column desktop, use lg:grid-cols-4 and consider shorter descriptions to keep cards balanced.
Frame-fit check
Each feature title should name what is ENFORCED, not what is included. "Authentication" is generic; "Identity from session, not request body" names the actual architectural commitment. The cards are skim-fodder; the buyer who reads one and decides to scroll further is the buyer the cards are for.