Componentdata1.04 KB gzipZero dependencies
Feature Vote
Idea rows with vote counts.
Preview
Live Preview
Vote the board
- Offline scanning mode
- Group seat picker
- Wallet pass export
Installation
Tokens guide →Add to your project via CLI (zero dependencies, code you own):
npx bigbullui add feature-voteOr install the full npm package:
npm install bigbulluiUsage
Open in StackBlitzimport { FeatureVote } from "@/components/ui/feature-vote";
<FeatureVote />Usage guidance
Use Feature Vote when a ticket stub surface needs Idea rows with vote counts. Keep props minimal and prefer composition with sibling stubs.
Avoid Feature Vote for long form flows or dense data grids where a dedicated layout block fits better.
Source code
feature-vote.tsxZero dependencies (only React + utils). Copy and paste directly into your project:
"use client";
import * as React from "react";
import { cn } from "./lib/utils";
export interface FeatureIdea {
id: string;
title: string;
votes: number;
}
export interface FeatureVoteProps extends React.HTMLAttributes<HTMLDivElement> {
initial?: FeatureIdea[];
}
/** Feature vote: idea rows with toggleable vote counts. */
export function FeatureVote({
initial = [
{ id: "fv1", title: "Offline scanning mode", votes: 214 },
{ id: "fv2", title: "Group seat picker", votes: 167 },
{ id: "fv3", title: "Wallet pass export", votes: 89 },
],
className,
...props
}: FeatureVoteProps) {
const [ideas, setIdeas] = React.useState(initial);
const [voted, setVoted] = React.useState<Set<string>>(new Set());
const toggle = (id: string) => {
setVoted((prev) => {
const next = new Set(prev);
const has = next.has(id);
if (has) next.delete(id);
else next.add(id);
setIdeas((list) => list.map((i) => (i.id === id ? { ...i, votes: i.votes + (has ? -1 : 1) } : i)));
return next;
});
};
return (
<div className={cn("w-full max-w-sm rounded-lg border-2 border-foreground bg-card p-4", className)} {...props}>
<p className="font-mono text-[10px] uppercase tracking-widest text-muted-foreground">Vote the board</p>
<ul className="mt-2 space-y-1.5">
{ideas.map((idea) => {
const mine = voted.has(idea.id);
return (
<li key={idea.id} className={cn("flex items-center justify-between gap-2 rounded-md border px-2.5 py-2", mine ? "border-accent" : "border-dashed border-border")}>
<span className="text-xs font-bold">{idea.title}</span>
<button
type="button"
onClick={() => toggle(idea.id)}
aria-pressed={mine}
aria-label={`Vote for ${idea.title}`}
className={cn(
"shrink-0 rounded border px-2 py-1 font-mono text-[11px] font-bold tabular-nums transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring motion-reduce:transition-none",
mine ? "border-accent bg-accent text-accent-foreground" : "border-border hover:border-accent hover:text-accent"
)}
>
▲ {idea.votes}
</button>
</li>
);
})}
</ul>
</div>
);
}