{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "masai-button", "title": "Masai Button", "description": "Reusable CTA button with primary/secondary/tertiary/link types, icon support, and sm/md/lg sizes.", "dependencies": [ "class-variance-authority" ], "files": [ { "path": "registry/components/masai-button.tsx", "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { cn } from \"@/lib/utils\";\n\ntype ButtonKind = \"primary\" | \"secondary\" | \"tertiary\" | \"link\";\ntype ButtonSize = \"sm\" | \"md\" | \"lg\";\ntype IconDirection = \"left\" | \"right\";\n\nconst buttonVariants = cva(\n \"inline-flex cursor-pointer items-center justify-center rounded-lg transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-400 disabled:cursor-not-allowed disabled:pointer-events-none disabled:opacity-30\",\n {\n variants: {\n kind: {\n primary: \"bg-primary-600 !text-white hover:bg-primary-700\",\n secondary:\n \"bg-purple-50 !text-[#6962AC] hover:bg-purple-100\",\n tertiary: \"bg-transparent !text-[#6962AC] hover:bg-purple-50\",\n link: \"bg-transparent !text-[#6962AC] hover:!text-[#6962AC] underline-offset-4 hover:underline\",\n },\n size: {\n sm: \"py-2 px-3 type-b2-md gap-1.5 [&_svg]:size-3.5\",\n md: \"py-2.5 px-4 type-b1-md gap-2 [&_svg]:size-5\",\n lg: \"py-3 px-5 type-b1-md gap-2 [&_svg]:size-5\",\n },\n iconOnly: {\n true: \"px-0\",\n false: \"\",\n },\n },\n compoundVariants: [\n { size: \"sm\", iconOnly: true, className: \"h-9 w-9\" },\n { size: \"md\", iconOnly: true, className: \"h-11 w-11\" },\n { size: \"lg\", iconOnly: true, className: \"h-12 w-12\" },\n { kind: \"link\", size: \"sm\", className: \"h-auto py-0 px-0 type-b2-md\" },\n { kind: \"link\", size: \"md\", className: \"h-auto py-0 px-0 type-b1-md\" },\n { kind: \"link\", size: \"lg\", className: \"h-auto py-0 px-0 type-s3\" },\n { kind: \"link\", iconOnly: true, className: \"h-auto w-auto\" },\n ],\n defaultVariants: {\n kind: \"primary\",\n size: \"md\",\n iconOnly: false,\n },\n },\n);\n\nexport type MasaiButtonProps = Omit<\n React.ButtonHTMLAttributes,\n \"type\"\n> &\n VariantProps & {\n /** Visual style in design system terms. */\n type?: ButtonKind;\n /** Sizing token from design system. */\n size?: ButtonSize;\n /** Optional icon element (for example from lucide-react). */\n icon?: React.ReactNode;\n /** Required when icon is present with label. */\n iconDirection?: IconDirection;\n /** Button label. Ignored when iconOnly=true. */\n ctaText?: string;\n /** If true, only icon is shown (ctaText is ignored). */\n iconOnly?: boolean;\n /** Native button type attribute. */\n htmlType?: \"button\" | \"submit\" | \"reset\";\n };\n\nexport type ButtonProps = MasaiButtonProps;\n\n/**\n * Masai reusable CTA button.\n * - if iconDirection is provided, icon should also be provided\n * - if iconOnly=true, only icon is rendered and ctaText is ignored\n */\nexport function MasaiButton({\n className,\n type = \"primary\",\n size = \"md\",\n icon,\n iconDirection,\n disabled = false,\n ctaText,\n onClick,\n iconOnly = false,\n htmlType = \"button\",\n ...props\n}: MasaiButtonProps) {\n if (!icon && iconDirection) {\n // Keep this non-throwing to avoid crashing consumers.\n // eslint-disable-next-line no-console\n console.warn(\n \"[Button] `iconDirection` was passed without `icon`. The direction will be ignored.\",\n );\n }\n\n const showIcon = Boolean(icon);\n const showText = !iconOnly && Boolean(ctaText);\n const effectiveIconDirection: IconDirection = iconDirection ?? \"left\";\n\n return (\n \n {showIcon && effectiveIconDirection === \"left\" ? icon : null}\n {showText ? ctaText : null}\n {showIcon && effectiveIconDirection === \"right\" ? icon : null}\n \n );\n}\n\n// Backward-compatible alias for existing imports.\nexport const Button = MasaiButton;\n", "type": "registry:ui" } ], "type": "registry:ui" }