@fpkit/acss 3.2.1 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,8 @@
1
+ import { a } from './chunk-DDSXKOUB.js';
2
+ import p from 'react';
3
+
4
+ function i(...e){return e.filter(Boolean).join(" ")}var C={CARD:"card",TITLE:"card-title",CONTENT:"card-content",FOOTER:"card-footer"};function y(e,r){r&&(e.key==="Enter"||e.key===" ")&&(e.preventDefault(),r(e));}var f=({children:e,className:r,style:o,as:t="h3",...a$1})=>p.createElement(a,{as:t,classes:i(C.TITLE,r),styles:o,...a$1},e);f.displayName="Card.Title";var b=({children:e,className:r,style:o,as:t="article",...a$1})=>p.createElement(a,{as:t,classes:i(C.CONTENT,r),styles:o,...a$1},e);b.displayName="Card.Content";var m=({children:e,className:r,style:o,as:t="div",...a$1})=>p.createElement(a,{as:t,classes:i(C.FOOTER,r),styles:o,...a$1},e);m.displayName="Card.Footer";var I=({as:e="div",styles:r,children:o,classes:t="shadow-sm",id:a$1,interactive:n=!1,onClick:s,tabIndex:u,role:c,"aria-label":T,"aria-labelledby":E,"aria-describedby":P,...v})=>(p.useEffect(()=>{},[s,n]),p.createElement(a,{as:e,id:a$1,styles:r,classes:t,"aria-label":T,"aria-labelledby":E,"aria-describedby":P,"data-card":n?"interactive":!0,...n?{role:c||"button",tabIndex:u??0,onClick:s,onKeyDown:x=>{(n||s)&&y(x,s);}}:{role:c,onClick:s},...v},o)),l=I;l.displayName="Card";l.Title=f;l.Content=b;l.Footer=m;var h=l;
5
+
6
+ export { f as a, b, m as c, l as d, h as e };
7
+ //# sourceMappingURL=out.js.map
8
+ //# sourceMappingURL=chunk-KAR3HDXK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/cards/card.tsx","../src/components/cards/card.utils.ts"],"names":["React","cn","classes","CARD_CLASSES","handleCardKeyDown","event","onClick","Title","children","className","style","as","props","ui_default","Content","Footer","CardRoot","styles","id","interactive","tabIndex","role","ariaLabel","ariaLabelledBy","ariaDescribedBy","Card","card_default"],"mappings":"wCAAA,OAAOA,MAAW,QCoBX,SAASC,KAAMC,EAAwD,CAC5E,OAAOA,EAAQ,OAAO,OAAO,EAAE,KAAK,GAAG,CACzC,CAMO,IAAMC,EAAe,CAC1B,KAAM,OACN,MAAO,aACP,QAAS,eACT,OAAQ,aACV,EAcO,SAASC,EACdC,EACAC,EACM,CACDA,IAGDD,EAAM,MAAQ,SAAWA,EAAM,MAAQ,OACzCA,EAAM,eAAe,EACrBC,EAAQD,CAAK,EAEjB,CDnBO,IAAME,EAAQ,CAAC,CACpB,SAAAC,EACA,UAAAC,EACA,MAAAC,EACA,GAAAC,EAAK,KACL,GAAGC,CACL,IAEIZ,EAAA,cAACa,EAAA,CACC,GAAIF,EACJ,QAASV,EAAGE,EAAa,MAAOM,CAAS,EACzC,OAAQC,EACP,GAAGE,GAEHJ,CACH,EAIJD,EAAM,YAAc,aA+Bb,IAAMO,EAAU,CAAC,CACtB,SAAAN,EACA,UAAAC,EACA,MAAAC,EACA,GAAAC,EAAK,UACL,GAAGC,CACL,IAEIZ,EAAA,cAACa,EAAA,CACC,GAAIF,EACJ,QAASV,EAAGE,EAAa,QAASM,CAAS,EAC3C,OAAQC,EACP,GAAGE,GAEHJ,CACH,EAIJM,EAAQ,YAAc,eA4Bf,IAAMC,EAAS,CAAC,CACrB,SAAAP,EACA,UAAAC,EACA,MAAAC,EACA,GAAAC,EAAK,MACL,GAAGC,CACL,IAEIZ,EAAA,cAACa,EAAA,CACC,GAAIF,EACJ,QAASV,EAAGE,EAAa,OAAQM,CAAS,EAC1C,OAAQC,EACP,GAAGE,GAEHJ,CACH,EAIJO,EAAO,YAAc,cAuErB,IAAMC,EAAW,CAAC,CAChB,GAAAL,EAAK,MACL,OAAAM,EACA,SAAAT,EACA,QAAAN,EAAU,YACV,GAAAgB,EACA,YAAAC,EAAc,GACd,QAAAb,EACA,SAAAc,EACA,KAAAC,EACA,aAAcC,EACd,kBAAmBC,EACnB,mBAAoBC,EACpB,GAAGZ,CACL,KAEEZ,EAAM,UAAU,IAAM,CAEtB,EAAG,CAACM,EAASa,CAAW,CAAC,EAsBvBnB,EAAA,cAACa,EAAA,CACC,GAAIF,EACJ,GAAIO,EACJ,OAAQD,EACR,QAASf,EACT,aAAYoB,EACZ,kBAAiBC,EACjB,mBAAkBC,EAClB,YAAWL,EAAc,cAAgB,GACxC,GAtBoBA,EACrB,CACE,KAAME,GAAQ,SACd,SAAUD,GAAY,EACtB,QAAAd,EACA,UAXiBD,GAA+B,EAChDc,GAAeb,IACjBF,EAAkBC,EAAOC,CAAO,CAEpC,CAQI,EACA,CACE,KAAAe,EACA,QAAAf,CACF,EAaC,GAAGM,GAEHJ,CACH,GAKSiB,EAAOT,EACpBS,EAAK,YAAc,OACnBA,EAAK,MAAQlB,EACbkB,EAAK,QAAUX,EACfW,EAAK,OAASV,EAEd,IAAOW,EAAQD","sourcesContent":["import React from \"react\";\nimport UI from \"#components/ui\";\nimport type {\n CardProps,\n CardTitleProps,\n CardContentProps,\n CardFooterProps,\n CardComponent,\n} from \"./card.types\";\nimport {\n cn,\n CARD_CLASSES,\n handleCardKeyDown,\n warnInteractiveUsage,\n} from \"./card.utils\";\n\n/**\n * Card.Title - Title sub-component for Card\n *\n * Renders a heading element for the card title. Defaults to h3 for proper\n * document outline, but can be customized via the `as` prop.\n *\n * ## Accessibility\n * - Use appropriate heading level based on document structure\n * - Combine with `aria-labelledby` on parent Card for accessible names\n *\n * @example\n * ```tsx\n * <Card aria-labelledby=\"card-title-1\">\n * <Card.Title id=\"card-title-1\">Featured Product</Card.Title>\n * </Card>\n * ```\n *\n * @example\n * ```tsx\n * // Custom heading level\n * <Card.Title as=\"h2\">Section Title</Card.Title>\n * ```\n */\nexport const Title = ({\n children,\n className,\n style,\n as = \"h3\",\n ...props\n}: CardTitleProps) => {\n return (\n <UI\n as={as}\n classes={cn(CARD_CLASSES.TITLE, className)}\n styles={style}\n {...props}\n >\n {children}\n </UI>\n );\n};\n\nTitle.displayName = \"Card.Title\";\n\n/**\n * Card.Content - Content sub-component for Card\n *\n * Renders the main content area of the card. Defaults to `<article>` for\n * standalone content, but can be changed to `div` or `section` via the `as` prop.\n *\n * ## Semantic HTML Guidelines\n * - Use `article` (default) for self-contained, syndicate-able content\n * - Use `div` for generic content containers\n * - Use `section` for thematic groupings with a heading\n *\n * @example\n * ```tsx\n * <Card>\n * <Card.Title>Article Title</Card.Title>\n * <Card.Content>\n * <p>This is standalone content...</p>\n * </Card.Content>\n * </Card>\n * ```\n *\n * @example\n * ```tsx\n * // Generic container (not standalone content)\n * <Card.Content as=\"div\">\n * <p>Generic content...</p>\n * </Card.Content>\n * ```\n */\nexport const Content = ({\n children,\n className,\n style,\n as = \"article\",\n ...props\n}: CardContentProps) => {\n return (\n <UI\n as={as}\n classes={cn(CARD_CLASSES.CONTENT, className)}\n styles={style}\n {...props}\n >\n {children}\n </UI>\n );\n};\n\nContent.displayName = \"Card.Content\";\n\n/**\n * Card.Footer - Footer sub-component for Card\n *\n * Renders a footer section for the card. Use for actions, metadata, or\n * supplementary information.\n *\n * @example\n * ```tsx\n * <Card>\n * <Card.Title>Product</Card.Title>\n * <Card.Content>Description...</Card.Content>\n * <Card.Footer>\n * <button>Add to Cart</button>\n * <span>$29.99</span>\n * </Card.Footer>\n * </Card>\n * ```\n *\n * @example\n * ```tsx\n * // Semantic footer element\n * <Card.Footer as=\"footer\">\n * <p>Last updated: 2024-01-15</p>\n * </Card.Footer>\n * ```\n */\nexport const Footer = ({\n children,\n className,\n style,\n as = \"div\",\n ...props\n}: CardFooterProps) => {\n return (\n <UI\n as={as}\n classes={cn(CARD_CLASSES.FOOTER, className)}\n styles={style}\n {...props}\n >\n {children}\n </UI>\n );\n};\n\nFooter.displayName = \"Card.Footer\";\n\n/**\n * Card - A flexible, accessible card component with compound component pattern\n *\n * The Card component provides a container for grouping related content and actions.\n * It follows the compound component pattern, exposing `Card.Title`, `Card.Content`,\n * and `Card.Footer` sub-components for structured layouts.\n *\n * ## Features\n * - **Polymorphic rendering**: Render as any HTML element via `as` prop\n * - **Compound components**: Structured sub-components for consistent layouts\n * - **Interactive variant**: Built-in keyboard navigation and ARIA support\n * - **Fully accessible**: WCAG 2.1 AA compliant with proper semantic HTML\n *\n * ## Accessibility\n *\n * ### Non-Interactive Cards\n * - Use semantic HTML: `article` for standalone content, `section` for groupings\n * - Provide accessible names with `aria-labelledby` referencing the title\n *\n * ### Interactive Cards (Clickable)\n * - Set `interactive={true}` to enable keyboard navigation (Enter/Space)\n * - Provide accessible name via `aria-label` or `aria-labelledby`\n * - Ensure adequate focus indicators (handled by CSS)\n *\n * @example\n * // Basic card with compound components\n * ```tsx\n * <Card>\n * <Card.Title>Product Name</Card.Title>\n * <Card.Content>\n * <p>Product description goes here...</p>\n * </Card.Content>\n * <Card.Footer>\n * <button>Buy Now</button>\n * </Card.Footer>\n * </Card>\n * ```\n *\n * @example\n * // Accessible interactive card\n * ```tsx\n * <Card\n * interactive\n * aria-label=\"View product details\"\n * onClick={() => navigate('/product/123')}\n * >\n * <Card.Title>Product Name</Card.Title>\n * <Card.Content>Click anywhere to view details</Card.Content>\n * </Card>\n * ```\n *\n * @example\n * // Semantic article card with accessible name\n * ```tsx\n * <Card as=\"article\" aria-labelledby=\"article-title\">\n * <Card.Title id=\"article-title\">Article Headline</Card.Title>\n * <Card.Content>Article body...</Card.Content>\n * </Card>\n * ```\n *\n * @example\n * // Card as a landmark region\n * ```tsx\n * <Card role=\"region\" aria-label=\"Featured products\">\n * <Card.Title>Featured</Card.Title>\n * <Card.Content>...</Card.Content>\n * </Card>\n * ```\n */\nconst CardRoot = ({\n as = \"div\",\n styles,\n children,\n classes = \"shadow-sm\",\n id,\n interactive = false,\n onClick,\n tabIndex,\n role,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n \"aria-describedby\": ariaDescribedBy,\n ...props\n}: CardProps) => {\n // Development warnings for common accessibility issues\n React.useEffect(() => {\n warnInteractiveUsage(!!onClick, interactive);\n }, [onClick, interactive]);\n\n // Interactive card handling\n const handleKeyDown = (event: React.KeyboardEvent) => {\n if (interactive || onClick) {\n handleCardKeyDown(event, onClick);\n }\n };\n\n const interactiveProps = interactive\n ? {\n role: role || \"button\",\n tabIndex: tabIndex ?? 0,\n onClick,\n onKeyDown: handleKeyDown,\n }\n : {\n role,\n onClick,\n };\n\n return (\n <UI\n as={as}\n id={id}\n styles={styles}\n classes={classes}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={ariaDescribedBy}\n data-card={interactive ? \"interactive\" : true}\n {...interactiveProps}\n {...props}\n >\n {children}\n </UI>\n );\n};\n\n// Create compound component with proper TypeScript typing\nexport const Card = CardRoot as CardComponent;\nCard.displayName = \"Card\";\nCard.Title = Title;\nCard.Content = Content;\nCard.Footer = Footer;\n\nexport default Card;\n\n// Export types for external consumption\nexport type {\n CardProps,\n CardTitleProps,\n CardContentProps,\n CardFooterProps,\n CardComponent,\n} from \"./card.types\";\n","/**\n * Utility functions for the Card component.\n */\n\n/**\n * Combines multiple className strings into a single string, filtering out falsy values.\n *\n * This utility provides a cleaner alternative to template literals for className composition,\n * avoiding unnecessary string allocation on every render.\n *\n * @param classes - Array of class names (can include undefined/null/empty strings)\n * @returns Combined className string\n *\n * @example\n * ```tsx\n * cn('card-title', className) // \"card-title custom-class\"\n * cn('card-title', undefined) // \"card-title\"\n * cn('card-title', '', 'extra') // \"card-title extra\"\n * ```\n */\nexport function cn(...classes: (string | undefined | null | false)[]): string {\n return classes.filter(Boolean).join(' ')\n}\n\n/**\n * CSS class name constants for Card components.\n * Centralizing these prevents typos and enables easier refactoring.\n */\nexport const CARD_CLASSES = {\n CARD: 'card',\n TITLE: 'card-title',\n CONTENT: 'card-content',\n FOOTER: 'card-footer',\n} as const\n\n/**\n * Handles keyboard events for interactive cards.\n * Triggers click handler on Enter or Space key press.\n *\n * @param event - Keyboard event\n * @param onClick - Click handler to invoke\n *\n * @example\n * ```tsx\n * <div onKeyDown={(e) => handleCardKeyDown(e, handleClick)}>\n * ```\n */\nexport function handleCardKeyDown(\n event: React.KeyboardEvent,\n onClick?: (event: React.MouseEvent | React.KeyboardEvent) => void\n): void {\n if (!onClick) return\n\n // Activate on Enter or Space (standard keyboard interaction pattern)\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault() // Prevent page scroll on Space\n onClick(event)\n }\n}\n\n/**\n * Development warning for interactive card usage.\n * Warns when onClick is provided without the interactive prop.\n *\n * This function is intentionally empty to comply with no-console linting rules.\n * In the future, consider using a proper logging/warning system.\n *\n * @param hasOnClick - Whether onClick handler is provided\n * @param isInteractive - Whether interactive prop is set\n */\nexport function warnInteractiveUsage(\n hasOnClick: boolean,\n isInteractive?: boolean\n): void {\n // Development warning removed to comply with ESLint no-console rule\n // TODO: Consider using a proper warning system if needed\n void hasOnClick\n void isInteractive\n}\n"]}
@@ -0,0 +1,18 @@
1
+ 'use strict';
2
+
3
+ var chunkEJ6KYBFE_cjs = require('./chunk-EJ6KYBFE.cjs');
4
+ var p = require('react');
5
+
6
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
7
+
8
+ var p__default = /*#__PURE__*/_interopDefault(p);
9
+
10
+ function i(...e){return e.filter(Boolean).join(" ")}var C={CARD:"card",TITLE:"card-title",CONTENT:"card-content",FOOTER:"card-footer"};function y(e,r){r&&(e.key==="Enter"||e.key===" ")&&(e.preventDefault(),r(e));}var f=({children:e,className:r,style:o,as:t="h3",...a})=>p__default.default.createElement(chunkEJ6KYBFE_cjs.a,{as:t,classes:i(C.TITLE,r),styles:o,...a},e);f.displayName="Card.Title";var b=({children:e,className:r,style:o,as:t="article",...a})=>p__default.default.createElement(chunkEJ6KYBFE_cjs.a,{as:t,classes:i(C.CONTENT,r),styles:o,...a},e);b.displayName="Card.Content";var m=({children:e,className:r,style:o,as:t="div",...a})=>p__default.default.createElement(chunkEJ6KYBFE_cjs.a,{as:t,classes:i(C.FOOTER,r),styles:o,...a},e);m.displayName="Card.Footer";var I=({as:e="div",styles:r,children:o,classes:t="shadow-sm",id:a,interactive:n=!1,onClick:s,tabIndex:u,role:c,"aria-label":T,"aria-labelledby":E,"aria-describedby":P,...v})=>(p__default.default.useEffect(()=>{},[s,n]),p__default.default.createElement(chunkEJ6KYBFE_cjs.a,{as:e,id:a,styles:r,classes:t,"aria-label":T,"aria-labelledby":E,"aria-describedby":P,"data-card":n?"interactive":!0,...n?{role:c||"button",tabIndex:u??0,onClick:s,onKeyDown:x=>{(n||s)&&y(x,s);}}:{role:c,onClick:s},...v},o)),l=I;l.displayName="Card";l.Title=f;l.Content=b;l.Footer=m;var h=l;
11
+
12
+ exports.a = f;
13
+ exports.b = b;
14
+ exports.c = m;
15
+ exports.d = l;
16
+ exports.e = h;
17
+ //# sourceMappingURL=out.js.map
18
+ //# sourceMappingURL=chunk-M5JARVJD.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/cards/card.tsx","../src/components/cards/card.utils.ts"],"names":["React","cn","classes","CARD_CLASSES","handleCardKeyDown","event","onClick","Title","children","className","style","as","props","ui_default","Content","Footer","CardRoot","styles","id","interactive","tabIndex","role","ariaLabel","ariaLabelledBy","ariaDescribedBy","Card","card_default"],"mappings":"yCAAA,OAAOA,MAAW,QCoBX,SAASC,KAAMC,EAAwD,CAC5E,OAAOA,EAAQ,OAAO,OAAO,EAAE,KAAK,GAAG,CACzC,CAMO,IAAMC,EAAe,CAC1B,KAAM,OACN,MAAO,aACP,QAAS,eACT,OAAQ,aACV,EAcO,SAASC,EACdC,EACAC,EACM,CACDA,IAGDD,EAAM,MAAQ,SAAWA,EAAM,MAAQ,OACzCA,EAAM,eAAe,EACrBC,EAAQD,CAAK,EAEjB,CDnBO,IAAME,EAAQ,CAAC,CACpB,SAAAC,EACA,UAAAC,EACA,MAAAC,EACA,GAAAC,EAAK,KACL,GAAGC,CACL,IAEIZ,EAAA,cAACa,EAAA,CACC,GAAIF,EACJ,QAASV,EAAGE,EAAa,MAAOM,CAAS,EACzC,OAAQC,EACP,GAAGE,GAEHJ,CACH,EAIJD,EAAM,YAAc,aA+Bb,IAAMO,EAAU,CAAC,CACtB,SAAAN,EACA,UAAAC,EACA,MAAAC,EACA,GAAAC,EAAK,UACL,GAAGC,CACL,IAEIZ,EAAA,cAACa,EAAA,CACC,GAAIF,EACJ,QAASV,EAAGE,EAAa,QAASM,CAAS,EAC3C,OAAQC,EACP,GAAGE,GAEHJ,CACH,EAIJM,EAAQ,YAAc,eA4Bf,IAAMC,EAAS,CAAC,CACrB,SAAAP,EACA,UAAAC,EACA,MAAAC,EACA,GAAAC,EAAK,MACL,GAAGC,CACL,IAEIZ,EAAA,cAACa,EAAA,CACC,GAAIF,EACJ,QAASV,EAAGE,EAAa,OAAQM,CAAS,EAC1C,OAAQC,EACP,GAAGE,GAEHJ,CACH,EAIJO,EAAO,YAAc,cAuErB,IAAMC,EAAW,CAAC,CAChB,GAAAL,EAAK,MACL,OAAAM,EACA,SAAAT,EACA,QAAAN,EAAU,YACV,GAAAgB,EACA,YAAAC,EAAc,GACd,QAAAb,EACA,SAAAc,EACA,KAAAC,EACA,aAAcC,EACd,kBAAmBC,EACnB,mBAAoBC,EACpB,GAAGZ,CACL,KAEEZ,EAAM,UAAU,IAAM,CAEtB,EAAG,CAACM,EAASa,CAAW,CAAC,EAsBvBnB,EAAA,cAACa,EAAA,CACC,GAAIF,EACJ,GAAIO,EACJ,OAAQD,EACR,QAASf,EACT,aAAYoB,EACZ,kBAAiBC,EACjB,mBAAkBC,EAClB,YAAWL,EAAc,cAAgB,GACxC,GAtBoBA,EACrB,CACE,KAAME,GAAQ,SACd,SAAUD,GAAY,EACtB,QAAAd,EACA,UAXiBD,GAA+B,EAChDc,GAAeb,IACjBF,EAAkBC,EAAOC,CAAO,CAEpC,CAQI,EACA,CACE,KAAAe,EACA,QAAAf,CACF,EAaC,GAAGM,GAEHJ,CACH,GAKSiB,EAAOT,EACpBS,EAAK,YAAc,OACnBA,EAAK,MAAQlB,EACbkB,EAAK,QAAUX,EACfW,EAAK,OAASV,EAEd,IAAOW,EAAQD","sourcesContent":["import React from \"react\";\nimport UI from \"#components/ui\";\nimport type {\n CardProps,\n CardTitleProps,\n CardContentProps,\n CardFooterProps,\n CardComponent,\n} from \"./card.types\";\nimport {\n cn,\n CARD_CLASSES,\n handleCardKeyDown,\n warnInteractiveUsage,\n} from \"./card.utils\";\n\n/**\n * Card.Title - Title sub-component for Card\n *\n * Renders a heading element for the card title. Defaults to h3 for proper\n * document outline, but can be customized via the `as` prop.\n *\n * ## Accessibility\n * - Use appropriate heading level based on document structure\n * - Combine with `aria-labelledby` on parent Card for accessible names\n *\n * @example\n * ```tsx\n * <Card aria-labelledby=\"card-title-1\">\n * <Card.Title id=\"card-title-1\">Featured Product</Card.Title>\n * </Card>\n * ```\n *\n * @example\n * ```tsx\n * // Custom heading level\n * <Card.Title as=\"h2\">Section Title</Card.Title>\n * ```\n */\nexport const Title = ({\n children,\n className,\n style,\n as = \"h3\",\n ...props\n}: CardTitleProps) => {\n return (\n <UI\n as={as}\n classes={cn(CARD_CLASSES.TITLE, className)}\n styles={style}\n {...props}\n >\n {children}\n </UI>\n );\n};\n\nTitle.displayName = \"Card.Title\";\n\n/**\n * Card.Content - Content sub-component for Card\n *\n * Renders the main content area of the card. Defaults to `<article>` for\n * standalone content, but can be changed to `div` or `section` via the `as` prop.\n *\n * ## Semantic HTML Guidelines\n * - Use `article` (default) for self-contained, syndicate-able content\n * - Use `div` for generic content containers\n * - Use `section` for thematic groupings with a heading\n *\n * @example\n * ```tsx\n * <Card>\n * <Card.Title>Article Title</Card.Title>\n * <Card.Content>\n * <p>This is standalone content...</p>\n * </Card.Content>\n * </Card>\n * ```\n *\n * @example\n * ```tsx\n * // Generic container (not standalone content)\n * <Card.Content as=\"div\">\n * <p>Generic content...</p>\n * </Card.Content>\n * ```\n */\nexport const Content = ({\n children,\n className,\n style,\n as = \"article\",\n ...props\n}: CardContentProps) => {\n return (\n <UI\n as={as}\n classes={cn(CARD_CLASSES.CONTENT, className)}\n styles={style}\n {...props}\n >\n {children}\n </UI>\n );\n};\n\nContent.displayName = \"Card.Content\";\n\n/**\n * Card.Footer - Footer sub-component for Card\n *\n * Renders a footer section for the card. Use for actions, metadata, or\n * supplementary information.\n *\n * @example\n * ```tsx\n * <Card>\n * <Card.Title>Product</Card.Title>\n * <Card.Content>Description...</Card.Content>\n * <Card.Footer>\n * <button>Add to Cart</button>\n * <span>$29.99</span>\n * </Card.Footer>\n * </Card>\n * ```\n *\n * @example\n * ```tsx\n * // Semantic footer element\n * <Card.Footer as=\"footer\">\n * <p>Last updated: 2024-01-15</p>\n * </Card.Footer>\n * ```\n */\nexport const Footer = ({\n children,\n className,\n style,\n as = \"div\",\n ...props\n}: CardFooterProps) => {\n return (\n <UI\n as={as}\n classes={cn(CARD_CLASSES.FOOTER, className)}\n styles={style}\n {...props}\n >\n {children}\n </UI>\n );\n};\n\nFooter.displayName = \"Card.Footer\";\n\n/**\n * Card - A flexible, accessible card component with compound component pattern\n *\n * The Card component provides a container for grouping related content and actions.\n * It follows the compound component pattern, exposing `Card.Title`, `Card.Content`,\n * and `Card.Footer` sub-components for structured layouts.\n *\n * ## Features\n * - **Polymorphic rendering**: Render as any HTML element via `as` prop\n * - **Compound components**: Structured sub-components for consistent layouts\n * - **Interactive variant**: Built-in keyboard navigation and ARIA support\n * - **Fully accessible**: WCAG 2.1 AA compliant with proper semantic HTML\n *\n * ## Accessibility\n *\n * ### Non-Interactive Cards\n * - Use semantic HTML: `article` for standalone content, `section` for groupings\n * - Provide accessible names with `aria-labelledby` referencing the title\n *\n * ### Interactive Cards (Clickable)\n * - Set `interactive={true}` to enable keyboard navigation (Enter/Space)\n * - Provide accessible name via `aria-label` or `aria-labelledby`\n * - Ensure adequate focus indicators (handled by CSS)\n *\n * @example\n * // Basic card with compound components\n * ```tsx\n * <Card>\n * <Card.Title>Product Name</Card.Title>\n * <Card.Content>\n * <p>Product description goes here...</p>\n * </Card.Content>\n * <Card.Footer>\n * <button>Buy Now</button>\n * </Card.Footer>\n * </Card>\n * ```\n *\n * @example\n * // Accessible interactive card\n * ```tsx\n * <Card\n * interactive\n * aria-label=\"View product details\"\n * onClick={() => navigate('/product/123')}\n * >\n * <Card.Title>Product Name</Card.Title>\n * <Card.Content>Click anywhere to view details</Card.Content>\n * </Card>\n * ```\n *\n * @example\n * // Semantic article card with accessible name\n * ```tsx\n * <Card as=\"article\" aria-labelledby=\"article-title\">\n * <Card.Title id=\"article-title\">Article Headline</Card.Title>\n * <Card.Content>Article body...</Card.Content>\n * </Card>\n * ```\n *\n * @example\n * // Card as a landmark region\n * ```tsx\n * <Card role=\"region\" aria-label=\"Featured products\">\n * <Card.Title>Featured</Card.Title>\n * <Card.Content>...</Card.Content>\n * </Card>\n * ```\n */\nconst CardRoot = ({\n as = \"div\",\n styles,\n children,\n classes = \"shadow-sm\",\n id,\n interactive = false,\n onClick,\n tabIndex,\n role,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n \"aria-describedby\": ariaDescribedBy,\n ...props\n}: CardProps) => {\n // Development warnings for common accessibility issues\n React.useEffect(() => {\n warnInteractiveUsage(!!onClick, interactive);\n }, [onClick, interactive]);\n\n // Interactive card handling\n const handleKeyDown = (event: React.KeyboardEvent) => {\n if (interactive || onClick) {\n handleCardKeyDown(event, onClick);\n }\n };\n\n const interactiveProps = interactive\n ? {\n role: role || \"button\",\n tabIndex: tabIndex ?? 0,\n onClick,\n onKeyDown: handleKeyDown,\n }\n : {\n role,\n onClick,\n };\n\n return (\n <UI\n as={as}\n id={id}\n styles={styles}\n classes={classes}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={ariaDescribedBy}\n data-card={interactive ? \"interactive\" : true}\n {...interactiveProps}\n {...props}\n >\n {children}\n </UI>\n );\n};\n\n// Create compound component with proper TypeScript typing\nexport const Card = CardRoot as CardComponent;\nCard.displayName = \"Card\";\nCard.Title = Title;\nCard.Content = Content;\nCard.Footer = Footer;\n\nexport default Card;\n\n// Export types for external consumption\nexport type {\n CardProps,\n CardTitleProps,\n CardContentProps,\n CardFooterProps,\n CardComponent,\n} from \"./card.types\";\n","/**\n * Utility functions for the Card component.\n */\n\n/**\n * Combines multiple className strings into a single string, filtering out falsy values.\n *\n * This utility provides a cleaner alternative to template literals for className composition,\n * avoiding unnecessary string allocation on every render.\n *\n * @param classes - Array of class names (can include undefined/null/empty strings)\n * @returns Combined className string\n *\n * @example\n * ```tsx\n * cn('card-title', className) // \"card-title custom-class\"\n * cn('card-title', undefined) // \"card-title\"\n * cn('card-title', '', 'extra') // \"card-title extra\"\n * ```\n */\nexport function cn(...classes: (string | undefined | null | false)[]): string {\n return classes.filter(Boolean).join(' ')\n}\n\n/**\n * CSS class name constants for Card components.\n * Centralizing these prevents typos and enables easier refactoring.\n */\nexport const CARD_CLASSES = {\n CARD: 'card',\n TITLE: 'card-title',\n CONTENT: 'card-content',\n FOOTER: 'card-footer',\n} as const\n\n/**\n * Handles keyboard events for interactive cards.\n * Triggers click handler on Enter or Space key press.\n *\n * @param event - Keyboard event\n * @param onClick - Click handler to invoke\n *\n * @example\n * ```tsx\n * <div onKeyDown={(e) => handleCardKeyDown(e, handleClick)}>\n * ```\n */\nexport function handleCardKeyDown(\n event: React.KeyboardEvent,\n onClick?: (event: React.MouseEvent | React.KeyboardEvent) => void\n): void {\n if (!onClick) return\n\n // Activate on Enter or Space (standard keyboard interaction pattern)\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault() // Prevent page scroll on Space\n onClick(event)\n }\n}\n\n/**\n * Development warning for interactive card usage.\n * Warns when onClick is provided without the interactive prop.\n *\n * This function is intentionally empty to comply with no-console linting rules.\n * In the future, consider using a proper logging/warning system.\n *\n * @param hasOnClick - Whether onClick handler is provided\n * @param isInteractive - Whether interactive prop is set\n */\nexport function warnInteractiveUsage(\n hasOnClick: boolean,\n isInteractive?: boolean\n): void {\n // Development warning removed to comply with ESLint no-console rule\n // TODO: Consider using a proper warning system if needed\n void hasOnClick\n void isInteractive\n}\n"]}
@@ -2,30 +2,30 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var chunkWWPLBWCQ_cjs = require('../chunk-WWPLBWCQ.cjs');
5
+ var chunkM5JARVJD_cjs = require('../chunk-M5JARVJD.cjs');
6
6
  require('../chunk-EJ6KYBFE.cjs');
7
7
 
8
8
 
9
9
 
10
10
  Object.defineProperty(exports, 'Card', {
11
11
  enumerable: true,
12
- get: function () { return chunkWWPLBWCQ_cjs.d; }
12
+ get: function () { return chunkM5JARVJD_cjs.d; }
13
13
  });
14
14
  Object.defineProperty(exports, 'Content', {
15
15
  enumerable: true,
16
- get: function () { return chunkWWPLBWCQ_cjs.b; }
16
+ get: function () { return chunkM5JARVJD_cjs.b; }
17
17
  });
18
18
  Object.defineProperty(exports, 'Footer', {
19
19
  enumerable: true,
20
- get: function () { return chunkWWPLBWCQ_cjs.c; }
20
+ get: function () { return chunkM5JARVJD_cjs.c; }
21
21
  });
22
22
  Object.defineProperty(exports, 'Title', {
23
23
  enumerable: true,
24
- get: function () { return chunkWWPLBWCQ_cjs.a; }
24
+ get: function () { return chunkM5JARVJD_cjs.a; }
25
25
  });
26
26
  Object.defineProperty(exports, 'default', {
27
27
  enumerable: true,
28
- get: function () { return chunkWWPLBWCQ_cjs.e; }
28
+ get: function () { return chunkM5JARVJD_cjs.e; }
29
29
  });
30
30
  //# sourceMappingURL=out.js.map
31
31
  //# sourceMappingURL=card.cjs.map
@@ -1,4 +1,4 @@
1
- export { d as Card, b as Content, c as Footer, a as Title, e as default } from '../chunk-OU52NIKA.js';
1
+ export { d as Card, b as Content, c as Footer, a as Title, e as default } from '../chunk-KAR3HDXK.js';
2
2
  import '../chunk-DDSXKOUB.js';
3
3
  //# sourceMappingURL=out.js.map
4
4
  //# sourceMappingURL=card.js.map
package/libs/index.cjs CHANGED
@@ -12,7 +12,7 @@ var chunk2C3YLBWP_cjs = require('./chunk-2C3YLBWP.cjs');
12
12
  var chunkLXODKKA3_cjs = require('./chunk-LXODKKA3.cjs');
13
13
  require('./chunk-PDD4N5P5.cjs');
14
14
  var chunk5CJPTDK3_cjs = require('./chunk-5CJPTDK3.cjs');
15
- var chunkWWPLBWCQ_cjs = require('./chunk-WWPLBWCQ.cjs');
15
+ var chunkM5JARVJD_cjs = require('./chunk-M5JARVJD.cjs');
16
16
  var chunkF64GE6RG_cjs = require('./chunk-F64GE6RG.cjs');
17
17
  var chunk5QSNJQVH_cjs = require('./chunk-5QSNJQVH.cjs');
18
18
  var chunk4BZKFPEC_cjs = require('./chunk-4BZKFPEC.cjs');
@@ -127,19 +127,19 @@ Object.defineProperty(exports, 'Icon', {
127
127
  });
128
128
  Object.defineProperty(exports, 'Card', {
129
129
  enumerable: true,
130
- get: function () { return chunkWWPLBWCQ_cjs.d; }
130
+ get: function () { return chunkM5JARVJD_cjs.d; }
131
131
  });
132
132
  Object.defineProperty(exports, 'CardContent', {
133
133
  enumerable: true,
134
- get: function () { return chunkWWPLBWCQ_cjs.b; }
134
+ get: function () { return chunkM5JARVJD_cjs.b; }
135
135
  });
136
136
  Object.defineProperty(exports, 'CardFooter', {
137
137
  enumerable: true,
138
- get: function () { return chunkWWPLBWCQ_cjs.c; }
138
+ get: function () { return chunkM5JARVJD_cjs.c; }
139
139
  });
140
140
  Object.defineProperty(exports, 'CardTitle', {
141
141
  enumerable: true,
142
- get: function () { return chunkWWPLBWCQ_cjs.a; }
142
+ get: function () { return chunkM5JARVJD_cjs.a; }
143
143
  });
144
144
  Object.defineProperty(exports, 'Modal', {
145
145
  enumerable: true,
package/libs/index.js CHANGED
@@ -12,7 +12,7 @@ export { b as Breadcrumb, a as useBreadcrumbSegments } from './chunk-DIJBIOFE.js
12
12
  import './chunk-GCGKYLDG.js';
13
13
  import { b as b$1 } from './chunk-M7JLT62Q.js';
14
14
  export { a as Icon } from './chunk-M7JLT62Q.js';
15
- export { d as Card, b as CardContent, c as CardFooter, a as CardTitle } from './chunk-OU52NIKA.js';
15
+ export { d as Card, b as CardContent, c as CardFooter, a as CardTitle } from './chunk-KAR3HDXK.js';
16
16
  export { a as Modal } from './chunk-PMWL5XZ4.js';
17
17
  import { b } from './chunk-TF3GQKOY.js';
18
18
  export { a as Button } from './chunk-TF3GQKOY.js';
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@fpkit/acss",
3
3
  "description": "A lightweight React UI library for building modern and accessible components that leverage CSS custom properties for reactive Styles.",
4
4
  "private": false,
5
- "version": "3.2.1",
5
+ "version": "3.3.0",
6
6
  "engines": {
7
7
  "node": ">=22.12.0",
8
8
  "npm": ">=8.0.0"
@@ -126,5 +126,5 @@
126
126
  "publishConfig": {
127
127
  "access": "public"
128
128
  },
129
- "gitHead": "460bda626ae4e23dbd203806f1d99d5616936172"
129
+ "gitHead": "1948e0adc68133b7c44f0ba3ffda118d8b0c1391"
130
130
  }
@@ -10,7 +10,7 @@ const meta: Meta<typeof Card> = {
10
10
  component: Card,
11
11
  args: {
12
12
  children: <p>{content}</p>,
13
- classes: "shadow-md",
13
+ classes: "shadow-sm rounded-lg",
14
14
  },
15
15
  argTypes: {
16
16
  as: {
@@ -1,13 +1,18 @@
1
- import React from 'react'
2
- import UI from '#components/ui'
1
+ import React from "react";
2
+ import UI from "#components/ui";
3
3
  import type {
4
4
  CardProps,
5
5
  CardTitleProps,
6
6
  CardContentProps,
7
7
  CardFooterProps,
8
8
  CardComponent,
9
- } from './card.types'
10
- import { cn, CARD_CLASSES, handleCardKeyDown, warnInteractiveUsage } from './card.utils'
9
+ } from "./card.types";
10
+ import {
11
+ cn,
12
+ CARD_CLASSES,
13
+ handleCardKeyDown,
14
+ warnInteractiveUsage,
15
+ } from "./card.utils";
11
16
 
12
17
  /**
13
18
  * Card.Title - Title sub-component for Card
@@ -36,22 +41,22 @@ export const Title = ({
36
41
  children,
37
42
  className,
38
43
  style,
39
- as = 'h3',
44
+ as = "h3",
40
45
  ...props
41
46
  }: CardTitleProps) => {
42
47
  return (
43
48
  <UI
44
49
  as={as}
45
- className={cn(CARD_CLASSES.TITLE, className)}
50
+ classes={cn(CARD_CLASSES.TITLE, className)}
46
51
  styles={style}
47
52
  {...props}
48
53
  >
49
54
  {children}
50
55
  </UI>
51
- )
52
- }
56
+ );
57
+ };
53
58
 
54
- Title.displayName = 'Card.Title'
59
+ Title.displayName = "Card.Title";
55
60
 
56
61
  /**
57
62
  * Card.Content - Content sub-component for Card
@@ -86,22 +91,22 @@ export const Content = ({
86
91
  children,
87
92
  className,
88
93
  style,
89
- as = 'article',
94
+ as = "article",
90
95
  ...props
91
96
  }: CardContentProps) => {
92
97
  return (
93
98
  <UI
94
99
  as={as}
95
- className={cn(CARD_CLASSES.CONTENT, className)}
100
+ classes={cn(CARD_CLASSES.CONTENT, className)}
96
101
  styles={style}
97
102
  {...props}
98
103
  >
99
104
  {children}
100
105
  </UI>
101
- )
102
- }
106
+ );
107
+ };
103
108
 
104
- Content.displayName = 'Card.Content'
109
+ Content.displayName = "Card.Content";
105
110
 
106
111
  /**
107
112
  * Card.Footer - Footer sub-component for Card
@@ -133,22 +138,22 @@ export const Footer = ({
133
138
  children,
134
139
  className,
135
140
  style,
136
- as = 'div',
141
+ as = "div",
137
142
  ...props
138
143
  }: CardFooterProps) => {
139
144
  return (
140
145
  <UI
141
146
  as={as}
142
- className={cn(CARD_CLASSES.FOOTER, className)}
147
+ classes={cn(CARD_CLASSES.FOOTER, className)}
143
148
  styles={style}
144
149
  {...props}
145
150
  >
146
151
  {children}
147
152
  </UI>
148
- )
149
- }
153
+ );
154
+ };
150
155
 
151
- Footer.displayName = 'Card.Footer'
156
+ Footer.displayName = "Card.Footer";
152
157
 
153
158
  /**
154
159
  * Card - A flexible, accessible card component with compound component pattern
@@ -220,35 +225,35 @@ Footer.displayName = 'Card.Footer'
220
225
  * ```
221
226
  */
222
227
  const CardRoot = ({
223
- as = 'div',
228
+ as = "div",
224
229
  styles,
225
230
  children,
226
- classes = 'shadow-sm',
231
+ classes = "shadow-sm",
227
232
  id,
228
233
  interactive = false,
229
234
  onClick,
230
235
  tabIndex,
231
236
  role,
232
- 'aria-label': ariaLabel,
233
- 'aria-labelledby': ariaLabelledBy,
234
- 'aria-describedby': ariaDescribedBy,
237
+ "aria-label": ariaLabel,
238
+ "aria-labelledby": ariaLabelledBy,
239
+ "aria-describedby": ariaDescribedBy,
235
240
  ...props
236
241
  }: CardProps) => {
237
242
  // Development warnings for common accessibility issues
238
243
  React.useEffect(() => {
239
- warnInteractiveUsage(!!onClick, interactive)
240
- }, [onClick, interactive])
244
+ warnInteractiveUsage(!!onClick, interactive);
245
+ }, [onClick, interactive]);
241
246
 
242
247
  // Interactive card handling
243
248
  const handleKeyDown = (event: React.KeyboardEvent) => {
244
249
  if (interactive || onClick) {
245
- handleCardKeyDown(event, onClick)
250
+ handleCardKeyDown(event, onClick);
246
251
  }
247
- }
252
+ };
248
253
 
249
254
  const interactiveProps = interactive
250
255
  ? {
251
- role: role || 'button',
256
+ role: role || "button",
252
257
  tabIndex: tabIndex ?? 0,
253
258
  onClick,
254
259
  onKeyDown: handleKeyDown,
@@ -256,34 +261,34 @@ const CardRoot = ({
256
261
  : {
257
262
  role,
258
263
  onClick,
259
- }
264
+ };
260
265
 
261
266
  return (
262
267
  <UI
263
268
  as={as}
264
269
  id={id}
265
270
  styles={styles}
266
- className={classes}
271
+ classes={classes}
267
272
  aria-label={ariaLabel}
268
273
  aria-labelledby={ariaLabelledBy}
269
274
  aria-describedby={ariaDescribedBy}
270
- data-card={interactive ? 'interactive' : true}
275
+ data-card={interactive ? "interactive" : true}
271
276
  {...interactiveProps}
272
277
  {...props}
273
278
  >
274
279
  {children}
275
280
  </UI>
276
- )
277
- }
281
+ );
282
+ };
278
283
 
279
284
  // Create compound component with proper TypeScript typing
280
- export const Card = CardRoot as CardComponent
281
- Card.displayName = 'Card'
282
- Card.Title = Title
283
- Card.Content = Content
284
- Card.Footer = Footer
285
+ export const Card = CardRoot as CardComponent;
286
+ Card.displayName = "Card";
287
+ Card.Title = Title;
288
+ Card.Content = Content;
289
+ Card.Footer = Footer;
285
290
 
286
- export default Card
291
+ export default Card;
287
292
 
288
293
  // Export types for external consumption
289
294
  export type {
@@ -292,4 +297,4 @@ export type {
292
297
  CardContentProps,
293
298
  CardFooterProps,
294
299
  CardComponent,
295
- } from './card.types'
300
+ } from "./card.types";
@@ -1,8 +0,0 @@
1
- import { a } from './chunk-DDSXKOUB.js';
2
- import p from 'react';
3
-
4
- function i(...e){return e.filter(Boolean).join(" ")}var C={CARD:"card",TITLE:"card-title",CONTENT:"card-content",FOOTER:"card-footer"};function y(e,r){r&&(e.key==="Enter"||e.key===" ")&&(e.preventDefault(),r(e));}var m=({children:e,className:r,style:a$1,as:o="h3",...t})=>p.createElement(a,{as:o,className:i(C.TITLE,r),styles:a$1,...t},e);m.displayName="Card.Title";var f=({children:e,className:r,style:a$1,as:o="article",...t})=>p.createElement(a,{as:o,className:i(C.CONTENT,r),styles:a$1,...t},e);f.displayName="Card.Content";var b=({children:e,className:r,style:a$1,as:o="div",...t})=>p.createElement(a,{as:o,className:i(C.FOOTER,r),styles:a$1,...t},e);b.displayName="Card.Footer";var x=({as:e="div",styles:r,children:a$1,classes:o="shadow-sm",id:t,interactive:s=!1,onClick:n,tabIndex:u,role:c,"aria-label":T,"aria-labelledby":E,"aria-describedby":P,...N})=>(p.useEffect(()=>{},[n,s]),p.createElement(a,{as:e,id:t,styles:r,className:o,"aria-label":T,"aria-labelledby":E,"aria-describedby":P,"data-card":s?"interactive":!0,...s?{role:c||"button",tabIndex:u??0,onClick:n,onKeyDown:v=>{(s||n)&&y(v,n);}}:{role:c,onClick:n},...N},a$1)),l=x;l.displayName="Card";l.Title=m;l.Content=f;l.Footer=b;var h=l;
5
-
6
- export { m as a, f as b, b as c, l as d, h as e };
7
- //# sourceMappingURL=out.js.map
8
- //# sourceMappingURL=chunk-OU52NIKA.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/cards/card.tsx","../src/components/cards/card.utils.ts"],"names":["React","cn","classes","CARD_CLASSES","handleCardKeyDown","event","onClick","Title","children","className","style","as","props","ui_default","Content","Footer","CardRoot","styles","id","interactive","tabIndex","role","ariaLabel","ariaLabelledBy","ariaDescribedBy","Card","card_default"],"mappings":"wCAAA,OAAOA,MAAW,QCoBX,SAASC,KAAMC,EAAwD,CAC5E,OAAOA,EAAQ,OAAO,OAAO,EAAE,KAAK,GAAG,CACzC,CAMO,IAAMC,EAAe,CAC1B,KAAM,OACN,MAAO,aACP,QAAS,eACT,OAAQ,aACV,EAcO,SAASC,EACdC,EACAC,EACM,CACDA,IAGDD,EAAM,MAAQ,SAAWA,EAAM,MAAQ,OACzCA,EAAM,eAAe,EACrBC,EAAQD,CAAK,EAEjB,CDxBO,IAAME,EAAQ,CAAC,CACpB,SAAAC,EACA,UAAAC,EACA,MAAAC,EACA,GAAAC,EAAK,KACL,GAAGC,CACL,IAEIZ,EAAA,cAACa,EAAA,CACC,GAAIF,EACJ,UAAWV,EAAGE,EAAa,MAAOM,CAAS,EAC3C,OAAQC,EACP,GAAGE,GAEHJ,CACH,EAIJD,EAAM,YAAc,aA+Bb,IAAMO,EAAU,CAAC,CACtB,SAAAN,EACA,UAAAC,EACA,MAAAC,EACA,GAAAC,EAAK,UACL,GAAGC,CACL,IAEIZ,EAAA,cAACa,EAAA,CACC,GAAIF,EACJ,UAAWV,EAAGE,EAAa,QAASM,CAAS,EAC7C,OAAQC,EACP,GAAGE,GAEHJ,CACH,EAIJM,EAAQ,YAAc,eA4Bf,IAAMC,EAAS,CAAC,CACrB,SAAAP,EACA,UAAAC,EACA,MAAAC,EACA,GAAAC,EAAK,MACL,GAAGC,CACL,IAEIZ,EAAA,cAACa,EAAA,CACC,GAAIF,EACJ,UAAWV,EAAGE,EAAa,OAAQM,CAAS,EAC5C,OAAQC,EACP,GAAGE,GAEHJ,CACH,EAIJO,EAAO,YAAc,cAuErB,IAAMC,EAAW,CAAC,CAChB,GAAAL,EAAK,MACL,OAAAM,EACA,SAAAT,EACA,QAAAN,EAAU,YACV,GAAAgB,EACA,YAAAC,EAAc,GACd,QAAAb,EACA,SAAAc,EACA,KAAAC,EACA,aAAcC,EACd,kBAAmBC,EACnB,mBAAoBC,EACpB,GAAGZ,CACL,KAEEZ,EAAM,UAAU,IAAM,CAEtB,EAAG,CAACM,EAASa,CAAW,CAAC,EAsBvBnB,EAAA,cAACa,EAAA,CACC,GAAIF,EACJ,GAAIO,EACJ,OAAQD,EACR,UAAWf,EACX,aAAYoB,EACZ,kBAAiBC,EACjB,mBAAkBC,EAClB,YAAWL,EAAc,cAAgB,GACxC,GAtBoBA,EACrB,CACE,KAAME,GAAQ,SACd,SAAUD,GAAY,EACtB,QAAAd,EACA,UAXiBD,GAA+B,EAChDc,GAAeb,IACjBF,EAAkBC,EAAOC,CAAO,CAEpC,CAQI,EACA,CACE,KAAAe,EACA,QAAAf,CACF,EAaC,GAAGM,GAEHJ,CACH,GAKSiB,EAAOT,EACpBS,EAAK,YAAc,OACnBA,EAAK,MAAQlB,EACbkB,EAAK,QAAUX,EACfW,EAAK,OAASV,EAEd,IAAOW,EAAQD","sourcesContent":["import React from 'react'\nimport UI from '#components/ui'\nimport type {\n CardProps,\n CardTitleProps,\n CardContentProps,\n CardFooterProps,\n CardComponent,\n} from './card.types'\nimport { cn, CARD_CLASSES, handleCardKeyDown, warnInteractiveUsage } from './card.utils'\n\n/**\n * Card.Title - Title sub-component for Card\n *\n * Renders a heading element for the card title. Defaults to h3 for proper\n * document outline, but can be customized via the `as` prop.\n *\n * ## Accessibility\n * - Use appropriate heading level based on document structure\n * - Combine with `aria-labelledby` on parent Card for accessible names\n *\n * @example\n * ```tsx\n * <Card aria-labelledby=\"card-title-1\">\n * <Card.Title id=\"card-title-1\">Featured Product</Card.Title>\n * </Card>\n * ```\n *\n * @example\n * ```tsx\n * // Custom heading level\n * <Card.Title as=\"h2\">Section Title</Card.Title>\n * ```\n */\nexport const Title = ({\n children,\n className,\n style,\n as = 'h3',\n ...props\n}: CardTitleProps) => {\n return (\n <UI\n as={as}\n className={cn(CARD_CLASSES.TITLE, className)}\n styles={style}\n {...props}\n >\n {children}\n </UI>\n )\n}\n\nTitle.displayName = 'Card.Title'\n\n/**\n * Card.Content - Content sub-component for Card\n *\n * Renders the main content area of the card. Defaults to `<article>` for\n * standalone content, but can be changed to `div` or `section` via the `as` prop.\n *\n * ## Semantic HTML Guidelines\n * - Use `article` (default) for self-contained, syndicate-able content\n * - Use `div` for generic content containers\n * - Use `section` for thematic groupings with a heading\n *\n * @example\n * ```tsx\n * <Card>\n * <Card.Title>Article Title</Card.Title>\n * <Card.Content>\n * <p>This is standalone content...</p>\n * </Card.Content>\n * </Card>\n * ```\n *\n * @example\n * ```tsx\n * // Generic container (not standalone content)\n * <Card.Content as=\"div\">\n * <p>Generic content...</p>\n * </Card.Content>\n * ```\n */\nexport const Content = ({\n children,\n className,\n style,\n as = 'article',\n ...props\n}: CardContentProps) => {\n return (\n <UI\n as={as}\n className={cn(CARD_CLASSES.CONTENT, className)}\n styles={style}\n {...props}\n >\n {children}\n </UI>\n )\n}\n\nContent.displayName = 'Card.Content'\n\n/**\n * Card.Footer - Footer sub-component for Card\n *\n * Renders a footer section for the card. Use for actions, metadata, or\n * supplementary information.\n *\n * @example\n * ```tsx\n * <Card>\n * <Card.Title>Product</Card.Title>\n * <Card.Content>Description...</Card.Content>\n * <Card.Footer>\n * <button>Add to Cart</button>\n * <span>$29.99</span>\n * </Card.Footer>\n * </Card>\n * ```\n *\n * @example\n * ```tsx\n * // Semantic footer element\n * <Card.Footer as=\"footer\">\n * <p>Last updated: 2024-01-15</p>\n * </Card.Footer>\n * ```\n */\nexport const Footer = ({\n children,\n className,\n style,\n as = 'div',\n ...props\n}: CardFooterProps) => {\n return (\n <UI\n as={as}\n className={cn(CARD_CLASSES.FOOTER, className)}\n styles={style}\n {...props}\n >\n {children}\n </UI>\n )\n}\n\nFooter.displayName = 'Card.Footer'\n\n/**\n * Card - A flexible, accessible card component with compound component pattern\n *\n * The Card component provides a container for grouping related content and actions.\n * It follows the compound component pattern, exposing `Card.Title`, `Card.Content`,\n * and `Card.Footer` sub-components for structured layouts.\n *\n * ## Features\n * - **Polymorphic rendering**: Render as any HTML element via `as` prop\n * - **Compound components**: Structured sub-components for consistent layouts\n * - **Interactive variant**: Built-in keyboard navigation and ARIA support\n * - **Fully accessible**: WCAG 2.1 AA compliant with proper semantic HTML\n *\n * ## Accessibility\n *\n * ### Non-Interactive Cards\n * - Use semantic HTML: `article` for standalone content, `section` for groupings\n * - Provide accessible names with `aria-labelledby` referencing the title\n *\n * ### Interactive Cards (Clickable)\n * - Set `interactive={true}` to enable keyboard navigation (Enter/Space)\n * - Provide accessible name via `aria-label` or `aria-labelledby`\n * - Ensure adequate focus indicators (handled by CSS)\n *\n * @example\n * // Basic card with compound components\n * ```tsx\n * <Card>\n * <Card.Title>Product Name</Card.Title>\n * <Card.Content>\n * <p>Product description goes here...</p>\n * </Card.Content>\n * <Card.Footer>\n * <button>Buy Now</button>\n * </Card.Footer>\n * </Card>\n * ```\n *\n * @example\n * // Accessible interactive card\n * ```tsx\n * <Card\n * interactive\n * aria-label=\"View product details\"\n * onClick={() => navigate('/product/123')}\n * >\n * <Card.Title>Product Name</Card.Title>\n * <Card.Content>Click anywhere to view details</Card.Content>\n * </Card>\n * ```\n *\n * @example\n * // Semantic article card with accessible name\n * ```tsx\n * <Card as=\"article\" aria-labelledby=\"article-title\">\n * <Card.Title id=\"article-title\">Article Headline</Card.Title>\n * <Card.Content>Article body...</Card.Content>\n * </Card>\n * ```\n *\n * @example\n * // Card as a landmark region\n * ```tsx\n * <Card role=\"region\" aria-label=\"Featured products\">\n * <Card.Title>Featured</Card.Title>\n * <Card.Content>...</Card.Content>\n * </Card>\n * ```\n */\nconst CardRoot = ({\n as = 'div',\n styles,\n children,\n classes = 'shadow-sm',\n id,\n interactive = false,\n onClick,\n tabIndex,\n role,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n ...props\n}: CardProps) => {\n // Development warnings for common accessibility issues\n React.useEffect(() => {\n warnInteractiveUsage(!!onClick, interactive)\n }, [onClick, interactive])\n\n // Interactive card handling\n const handleKeyDown = (event: React.KeyboardEvent) => {\n if (interactive || onClick) {\n handleCardKeyDown(event, onClick)\n }\n }\n\n const interactiveProps = interactive\n ? {\n role: role || 'button',\n tabIndex: tabIndex ?? 0,\n onClick,\n onKeyDown: handleKeyDown,\n }\n : {\n role,\n onClick,\n }\n\n return (\n <UI\n as={as}\n id={id}\n styles={styles}\n className={classes}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={ariaDescribedBy}\n data-card={interactive ? 'interactive' : true}\n {...interactiveProps}\n {...props}\n >\n {children}\n </UI>\n )\n}\n\n// Create compound component with proper TypeScript typing\nexport const Card = CardRoot as CardComponent\nCard.displayName = 'Card'\nCard.Title = Title\nCard.Content = Content\nCard.Footer = Footer\n\nexport default Card\n\n// Export types for external consumption\nexport type {\n CardProps,\n CardTitleProps,\n CardContentProps,\n CardFooterProps,\n CardComponent,\n} from './card.types'\n","/**\n * Utility functions for the Card component.\n */\n\n/**\n * Combines multiple className strings into a single string, filtering out falsy values.\n *\n * This utility provides a cleaner alternative to template literals for className composition,\n * avoiding unnecessary string allocation on every render.\n *\n * @param classes - Array of class names (can include undefined/null/empty strings)\n * @returns Combined className string\n *\n * @example\n * ```tsx\n * cn('card-title', className) // \"card-title custom-class\"\n * cn('card-title', undefined) // \"card-title\"\n * cn('card-title', '', 'extra') // \"card-title extra\"\n * ```\n */\nexport function cn(...classes: (string | undefined | null | false)[]): string {\n return classes.filter(Boolean).join(' ')\n}\n\n/**\n * CSS class name constants for Card components.\n * Centralizing these prevents typos and enables easier refactoring.\n */\nexport const CARD_CLASSES = {\n CARD: 'card',\n TITLE: 'card-title',\n CONTENT: 'card-content',\n FOOTER: 'card-footer',\n} as const\n\n/**\n * Handles keyboard events for interactive cards.\n * Triggers click handler on Enter or Space key press.\n *\n * @param event - Keyboard event\n * @param onClick - Click handler to invoke\n *\n * @example\n * ```tsx\n * <div onKeyDown={(e) => handleCardKeyDown(e, handleClick)}>\n * ```\n */\nexport function handleCardKeyDown(\n event: React.KeyboardEvent,\n onClick?: (event: React.MouseEvent | React.KeyboardEvent) => void\n): void {\n if (!onClick) return\n\n // Activate on Enter or Space (standard keyboard interaction pattern)\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault() // Prevent page scroll on Space\n onClick(event)\n }\n}\n\n/**\n * Development warning for interactive card usage.\n * Warns when onClick is provided without the interactive prop.\n *\n * This function is intentionally empty to comply with no-console linting rules.\n * In the future, consider using a proper logging/warning system.\n *\n * @param hasOnClick - Whether onClick handler is provided\n * @param isInteractive - Whether interactive prop is set\n */\nexport function warnInteractiveUsage(\n hasOnClick: boolean,\n isInteractive?: boolean\n): void {\n // Development warning removed to comply with ESLint no-console rule\n // TODO: Consider using a proper warning system if needed\n void hasOnClick\n void isInteractive\n}\n"]}
@@ -1,18 +0,0 @@
1
- 'use strict';
2
-
3
- var chunkEJ6KYBFE_cjs = require('./chunk-EJ6KYBFE.cjs');
4
- var p = require('react');
5
-
6
- function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
7
-
8
- var p__default = /*#__PURE__*/_interopDefault(p);
9
-
10
- function i(...e){return e.filter(Boolean).join(" ")}var C={CARD:"card",TITLE:"card-title",CONTENT:"card-content",FOOTER:"card-footer"};function y(e,r){r&&(e.key==="Enter"||e.key===" ")&&(e.preventDefault(),r(e));}var m=({children:e,className:r,style:a,as:o="h3",...t})=>p__default.default.createElement(chunkEJ6KYBFE_cjs.a,{as:o,className:i(C.TITLE,r),styles:a,...t},e);m.displayName="Card.Title";var f=({children:e,className:r,style:a,as:o="article",...t})=>p__default.default.createElement(chunkEJ6KYBFE_cjs.a,{as:o,className:i(C.CONTENT,r),styles:a,...t},e);f.displayName="Card.Content";var b=({children:e,className:r,style:a,as:o="div",...t})=>p__default.default.createElement(chunkEJ6KYBFE_cjs.a,{as:o,className:i(C.FOOTER,r),styles:a,...t},e);b.displayName="Card.Footer";var x=({as:e="div",styles:r,children:a,classes:o="shadow-sm",id:t,interactive:s=!1,onClick:n,tabIndex:u,role:c,"aria-label":T,"aria-labelledby":E,"aria-describedby":P,...N})=>(p__default.default.useEffect(()=>{},[n,s]),p__default.default.createElement(chunkEJ6KYBFE_cjs.a,{as:e,id:t,styles:r,className:o,"aria-label":T,"aria-labelledby":E,"aria-describedby":P,"data-card":s?"interactive":!0,...s?{role:c||"button",tabIndex:u??0,onClick:n,onKeyDown:v=>{(s||n)&&y(v,n);}}:{role:c,onClick:n},...N},a)),l=x;l.displayName="Card";l.Title=m;l.Content=f;l.Footer=b;var h=l;
11
-
12
- exports.a = m;
13
- exports.b = f;
14
- exports.c = b;
15
- exports.d = l;
16
- exports.e = h;
17
- //# sourceMappingURL=out.js.map
18
- //# sourceMappingURL=chunk-WWPLBWCQ.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/cards/card.tsx","../src/components/cards/card.utils.ts"],"names":["React","cn","classes","CARD_CLASSES","handleCardKeyDown","event","onClick","Title","children","className","style","as","props","ui_default","Content","Footer","CardRoot","styles","id","interactive","tabIndex","role","ariaLabel","ariaLabelledBy","ariaDescribedBy","Card","card_default"],"mappings":"yCAAA,OAAOA,MAAW,QCoBX,SAASC,KAAMC,EAAwD,CAC5E,OAAOA,EAAQ,OAAO,OAAO,EAAE,KAAK,GAAG,CACzC,CAMO,IAAMC,EAAe,CAC1B,KAAM,OACN,MAAO,aACP,QAAS,eACT,OAAQ,aACV,EAcO,SAASC,EACdC,EACAC,EACM,CACDA,IAGDD,EAAM,MAAQ,SAAWA,EAAM,MAAQ,OACzCA,EAAM,eAAe,EACrBC,EAAQD,CAAK,EAEjB,CDxBO,IAAME,EAAQ,CAAC,CACpB,SAAAC,EACA,UAAAC,EACA,MAAAC,EACA,GAAAC,EAAK,KACL,GAAGC,CACL,IAEIZ,EAAA,cAACa,EAAA,CACC,GAAIF,EACJ,UAAWV,EAAGE,EAAa,MAAOM,CAAS,EAC3C,OAAQC,EACP,GAAGE,GAEHJ,CACH,EAIJD,EAAM,YAAc,aA+Bb,IAAMO,EAAU,CAAC,CACtB,SAAAN,EACA,UAAAC,EACA,MAAAC,EACA,GAAAC,EAAK,UACL,GAAGC,CACL,IAEIZ,EAAA,cAACa,EAAA,CACC,GAAIF,EACJ,UAAWV,EAAGE,EAAa,QAASM,CAAS,EAC7C,OAAQC,EACP,GAAGE,GAEHJ,CACH,EAIJM,EAAQ,YAAc,eA4Bf,IAAMC,EAAS,CAAC,CACrB,SAAAP,EACA,UAAAC,EACA,MAAAC,EACA,GAAAC,EAAK,MACL,GAAGC,CACL,IAEIZ,EAAA,cAACa,EAAA,CACC,GAAIF,EACJ,UAAWV,EAAGE,EAAa,OAAQM,CAAS,EAC5C,OAAQC,EACP,GAAGE,GAEHJ,CACH,EAIJO,EAAO,YAAc,cAuErB,IAAMC,EAAW,CAAC,CAChB,GAAAL,EAAK,MACL,OAAAM,EACA,SAAAT,EACA,QAAAN,EAAU,YACV,GAAAgB,EACA,YAAAC,EAAc,GACd,QAAAb,EACA,SAAAc,EACA,KAAAC,EACA,aAAcC,EACd,kBAAmBC,EACnB,mBAAoBC,EACpB,GAAGZ,CACL,KAEEZ,EAAM,UAAU,IAAM,CAEtB,EAAG,CAACM,EAASa,CAAW,CAAC,EAsBvBnB,EAAA,cAACa,EAAA,CACC,GAAIF,EACJ,GAAIO,EACJ,OAAQD,EACR,UAAWf,EACX,aAAYoB,EACZ,kBAAiBC,EACjB,mBAAkBC,EAClB,YAAWL,EAAc,cAAgB,GACxC,GAtBoBA,EACrB,CACE,KAAME,GAAQ,SACd,SAAUD,GAAY,EACtB,QAAAd,EACA,UAXiBD,GAA+B,EAChDc,GAAeb,IACjBF,EAAkBC,EAAOC,CAAO,CAEpC,CAQI,EACA,CACE,KAAAe,EACA,QAAAf,CACF,EAaC,GAAGM,GAEHJ,CACH,GAKSiB,EAAOT,EACpBS,EAAK,YAAc,OACnBA,EAAK,MAAQlB,EACbkB,EAAK,QAAUX,EACfW,EAAK,OAASV,EAEd,IAAOW,EAAQD","sourcesContent":["import React from 'react'\nimport UI from '#components/ui'\nimport type {\n CardProps,\n CardTitleProps,\n CardContentProps,\n CardFooterProps,\n CardComponent,\n} from './card.types'\nimport { cn, CARD_CLASSES, handleCardKeyDown, warnInteractiveUsage } from './card.utils'\n\n/**\n * Card.Title - Title sub-component for Card\n *\n * Renders a heading element for the card title. Defaults to h3 for proper\n * document outline, but can be customized via the `as` prop.\n *\n * ## Accessibility\n * - Use appropriate heading level based on document structure\n * - Combine with `aria-labelledby` on parent Card for accessible names\n *\n * @example\n * ```tsx\n * <Card aria-labelledby=\"card-title-1\">\n * <Card.Title id=\"card-title-1\">Featured Product</Card.Title>\n * </Card>\n * ```\n *\n * @example\n * ```tsx\n * // Custom heading level\n * <Card.Title as=\"h2\">Section Title</Card.Title>\n * ```\n */\nexport const Title = ({\n children,\n className,\n style,\n as = 'h3',\n ...props\n}: CardTitleProps) => {\n return (\n <UI\n as={as}\n className={cn(CARD_CLASSES.TITLE, className)}\n styles={style}\n {...props}\n >\n {children}\n </UI>\n )\n}\n\nTitle.displayName = 'Card.Title'\n\n/**\n * Card.Content - Content sub-component for Card\n *\n * Renders the main content area of the card. Defaults to `<article>` for\n * standalone content, but can be changed to `div` or `section` via the `as` prop.\n *\n * ## Semantic HTML Guidelines\n * - Use `article` (default) for self-contained, syndicate-able content\n * - Use `div` for generic content containers\n * - Use `section` for thematic groupings with a heading\n *\n * @example\n * ```tsx\n * <Card>\n * <Card.Title>Article Title</Card.Title>\n * <Card.Content>\n * <p>This is standalone content...</p>\n * </Card.Content>\n * </Card>\n * ```\n *\n * @example\n * ```tsx\n * // Generic container (not standalone content)\n * <Card.Content as=\"div\">\n * <p>Generic content...</p>\n * </Card.Content>\n * ```\n */\nexport const Content = ({\n children,\n className,\n style,\n as = 'article',\n ...props\n}: CardContentProps) => {\n return (\n <UI\n as={as}\n className={cn(CARD_CLASSES.CONTENT, className)}\n styles={style}\n {...props}\n >\n {children}\n </UI>\n )\n}\n\nContent.displayName = 'Card.Content'\n\n/**\n * Card.Footer - Footer sub-component for Card\n *\n * Renders a footer section for the card. Use for actions, metadata, or\n * supplementary information.\n *\n * @example\n * ```tsx\n * <Card>\n * <Card.Title>Product</Card.Title>\n * <Card.Content>Description...</Card.Content>\n * <Card.Footer>\n * <button>Add to Cart</button>\n * <span>$29.99</span>\n * </Card.Footer>\n * </Card>\n * ```\n *\n * @example\n * ```tsx\n * // Semantic footer element\n * <Card.Footer as=\"footer\">\n * <p>Last updated: 2024-01-15</p>\n * </Card.Footer>\n * ```\n */\nexport const Footer = ({\n children,\n className,\n style,\n as = 'div',\n ...props\n}: CardFooterProps) => {\n return (\n <UI\n as={as}\n className={cn(CARD_CLASSES.FOOTER, className)}\n styles={style}\n {...props}\n >\n {children}\n </UI>\n )\n}\n\nFooter.displayName = 'Card.Footer'\n\n/**\n * Card - A flexible, accessible card component with compound component pattern\n *\n * The Card component provides a container for grouping related content and actions.\n * It follows the compound component pattern, exposing `Card.Title`, `Card.Content`,\n * and `Card.Footer` sub-components for structured layouts.\n *\n * ## Features\n * - **Polymorphic rendering**: Render as any HTML element via `as` prop\n * - **Compound components**: Structured sub-components for consistent layouts\n * - **Interactive variant**: Built-in keyboard navigation and ARIA support\n * - **Fully accessible**: WCAG 2.1 AA compliant with proper semantic HTML\n *\n * ## Accessibility\n *\n * ### Non-Interactive Cards\n * - Use semantic HTML: `article` for standalone content, `section` for groupings\n * - Provide accessible names with `aria-labelledby` referencing the title\n *\n * ### Interactive Cards (Clickable)\n * - Set `interactive={true}` to enable keyboard navigation (Enter/Space)\n * - Provide accessible name via `aria-label` or `aria-labelledby`\n * - Ensure adequate focus indicators (handled by CSS)\n *\n * @example\n * // Basic card with compound components\n * ```tsx\n * <Card>\n * <Card.Title>Product Name</Card.Title>\n * <Card.Content>\n * <p>Product description goes here...</p>\n * </Card.Content>\n * <Card.Footer>\n * <button>Buy Now</button>\n * </Card.Footer>\n * </Card>\n * ```\n *\n * @example\n * // Accessible interactive card\n * ```tsx\n * <Card\n * interactive\n * aria-label=\"View product details\"\n * onClick={() => navigate('/product/123')}\n * >\n * <Card.Title>Product Name</Card.Title>\n * <Card.Content>Click anywhere to view details</Card.Content>\n * </Card>\n * ```\n *\n * @example\n * // Semantic article card with accessible name\n * ```tsx\n * <Card as=\"article\" aria-labelledby=\"article-title\">\n * <Card.Title id=\"article-title\">Article Headline</Card.Title>\n * <Card.Content>Article body...</Card.Content>\n * </Card>\n * ```\n *\n * @example\n * // Card as a landmark region\n * ```tsx\n * <Card role=\"region\" aria-label=\"Featured products\">\n * <Card.Title>Featured</Card.Title>\n * <Card.Content>...</Card.Content>\n * </Card>\n * ```\n */\nconst CardRoot = ({\n as = 'div',\n styles,\n children,\n classes = 'shadow-sm',\n id,\n interactive = false,\n onClick,\n tabIndex,\n role,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n ...props\n}: CardProps) => {\n // Development warnings for common accessibility issues\n React.useEffect(() => {\n warnInteractiveUsage(!!onClick, interactive)\n }, [onClick, interactive])\n\n // Interactive card handling\n const handleKeyDown = (event: React.KeyboardEvent) => {\n if (interactive || onClick) {\n handleCardKeyDown(event, onClick)\n }\n }\n\n const interactiveProps = interactive\n ? {\n role: role || 'button',\n tabIndex: tabIndex ?? 0,\n onClick,\n onKeyDown: handleKeyDown,\n }\n : {\n role,\n onClick,\n }\n\n return (\n <UI\n as={as}\n id={id}\n styles={styles}\n className={classes}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={ariaDescribedBy}\n data-card={interactive ? 'interactive' : true}\n {...interactiveProps}\n {...props}\n >\n {children}\n </UI>\n )\n}\n\n// Create compound component with proper TypeScript typing\nexport const Card = CardRoot as CardComponent\nCard.displayName = 'Card'\nCard.Title = Title\nCard.Content = Content\nCard.Footer = Footer\n\nexport default Card\n\n// Export types for external consumption\nexport type {\n CardProps,\n CardTitleProps,\n CardContentProps,\n CardFooterProps,\n CardComponent,\n} from './card.types'\n","/**\n * Utility functions for the Card component.\n */\n\n/**\n * Combines multiple className strings into a single string, filtering out falsy values.\n *\n * This utility provides a cleaner alternative to template literals for className composition,\n * avoiding unnecessary string allocation on every render.\n *\n * @param classes - Array of class names (can include undefined/null/empty strings)\n * @returns Combined className string\n *\n * @example\n * ```tsx\n * cn('card-title', className) // \"card-title custom-class\"\n * cn('card-title', undefined) // \"card-title\"\n * cn('card-title', '', 'extra') // \"card-title extra\"\n * ```\n */\nexport function cn(...classes: (string | undefined | null | false)[]): string {\n return classes.filter(Boolean).join(' ')\n}\n\n/**\n * CSS class name constants for Card components.\n * Centralizing these prevents typos and enables easier refactoring.\n */\nexport const CARD_CLASSES = {\n CARD: 'card',\n TITLE: 'card-title',\n CONTENT: 'card-content',\n FOOTER: 'card-footer',\n} as const\n\n/**\n * Handles keyboard events for interactive cards.\n * Triggers click handler on Enter or Space key press.\n *\n * @param event - Keyboard event\n * @param onClick - Click handler to invoke\n *\n * @example\n * ```tsx\n * <div onKeyDown={(e) => handleCardKeyDown(e, handleClick)}>\n * ```\n */\nexport function handleCardKeyDown(\n event: React.KeyboardEvent,\n onClick?: (event: React.MouseEvent | React.KeyboardEvent) => void\n): void {\n if (!onClick) return\n\n // Activate on Enter or Space (standard keyboard interaction pattern)\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault() // Prevent page scroll on Space\n onClick(event)\n }\n}\n\n/**\n * Development warning for interactive card usage.\n * Warns when onClick is provided without the interactive prop.\n *\n * This function is intentionally empty to comply with no-console linting rules.\n * In the future, consider using a proper logging/warning system.\n *\n * @param hasOnClick - Whether onClick handler is provided\n * @param isInteractive - Whether interactive prop is set\n */\nexport function warnInteractiveUsage(\n hasOnClick: boolean,\n isInteractive?: boolean\n): void {\n // Development warning removed to comply with ESLint no-console rule\n // TODO: Consider using a proper warning system if needed\n void hasOnClick\n void isInteractive\n}\n"]}