@jiwambe/components 0.3.1 → 0.3.3

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.
Files changed (40) hide show
  1. package/README.md +84 -0
  2. package/dist/components/Box/Box.d.ts +10 -32
  3. package/dist/components/Box/Box.d.ts.map +1 -1
  4. package/dist/components/Box/Box.js +9 -13
  5. package/dist/components/Box/Box.js.map +1 -1
  6. package/dist/components/Card/Card.d.ts.map +1 -1
  7. package/dist/components/Card/Card.js +33 -32
  8. package/dist/components/Card/Card.js.map +1 -1
  9. package/dist/components/FAQ/FAQ.d.ts.map +1 -1
  10. package/dist/components/FAQ/FAQ.js +7 -7
  11. package/dist/components/FAQ/FAQ.js.map +1 -1
  12. package/dist/components/List/List.d.ts +10 -9
  13. package/dist/components/List/List.d.ts.map +1 -1
  14. package/dist/components/List/List.js +20 -5
  15. package/dist/components/List/List.js.map +1 -1
  16. package/dist/components/List/index.d.ts +1 -1
  17. package/dist/components/List/index.d.ts.map +1 -1
  18. package/dist/components/ProductCard/ProductCard.d.ts +81 -0
  19. package/dist/components/ProductCard/ProductCard.d.ts.map +1 -0
  20. package/dist/components/ProductCard/index.d.ts +3 -0
  21. package/dist/components/ProductCard/index.d.ts.map +1 -0
  22. package/dist/components/Typography/Typography.d.ts +10 -293
  23. package/dist/components/Typography/Typography.d.ts.map +1 -1
  24. package/dist/components/Typography/Typography.js +6 -11
  25. package/dist/components/Typography/Typography.js.map +1 -1
  26. package/dist/components/index.d.ts +3 -1
  27. package/dist/components/index.d.ts.map +1 -1
  28. package/dist/index.d.ts +1 -1
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/plugin/jiwambe-plugin.d.ts.map +1 -1
  31. package/dist/plugin/jiwambe-plugin.js +55 -2
  32. package/dist/plugin/jiwambe-plugin.js.map +1 -1
  33. package/dist/server.d.ts +1 -1
  34. package/dist/server.d.ts.map +1 -1
  35. package/dist/types/index.d.ts +0 -6
  36. package/dist/types/index.d.ts.map +1 -1
  37. package/dist/types/list.d.ts +21 -2
  38. package/dist/types/list.d.ts.map +1 -1
  39. package/dist/types/list.js.map +1 -1
  40. package/package.json +3 -2
package/README.md CHANGED
@@ -114,6 +114,7 @@ import { Box, Stack, Container, Grid } from '@jiwambe/components';
114
114
  | space-36 | var(--space-36) | 9rem |
115
115
  | space-48 | var(--space-48) | 12rem |
116
116
  | space-72 | var(--space-72) | 18rem |
117
+ | space-list-indent | var(--space-list-indent) | 1.6875rem (27px) |
117
118
  | fluid-1-2 | var(--space-fluid-1-2) | clamp(0.25rem, 0.1706rem + 0.3968vw, 0.5rem) |
118
119
  | fluid-2-4 | var(--space-fluid-2-4) | clamp(0.5rem, 0.3413rem + 0.7937vw, 1rem) |
119
120
  | fluid-4-5 | var(--space-fluid-4-5) | clamp(1rem, 0.9206rem + 0.3968vw, 1.25rem) |
@@ -260,6 +261,89 @@ Horizontal tabs with full ARIA support.
260
261
  | `activeIndex` | `number` | — | Active tab index (controlled). |
261
262
  | `onTabChange` | `(index: number) => void` | — | Called when the active tab changes (controlled). |
262
263
 
264
+ ### List
265
+
266
+ Semantic list with correct `ul`/`ol` structure. For `marker="disc"` and `marker="decimal"` uses block layout and item margin (not flex/gap) so markers render reliably. Use `indent` and `itemPadding="none"` for legal/body-copy lists.
267
+
268
+ ```tsx
269
+ <List spacing="space-2">
270
+ <List.Item size="sm">Item one</List.Item>
271
+ <List.Item size="sm">Item two</List.Item>
272
+ </List>
273
+
274
+ <List marker="decimal" spacing="space-3">
275
+ <List.Item size="md">Step one</List.Item>
276
+ <List.Item size="md">Step two</List.Item>
277
+ </List>
278
+
279
+ {/* Legal/body-copy: 27px indent, no extra item padding */}
280
+ <List indent="space-list-indent" itemPadding="none" spacing="space-2">
281
+ <List.Item size="md">First term.</List.Item>
282
+ <List.Item size="md">Second term.</List.Item>
283
+ </List>
284
+
285
+ <List marker="none" spacing="space-1">
286
+ <List.Item><Link href="/about">About</Link></List.Item>
287
+ </List>
288
+ ```
289
+
290
+ **Props:**
291
+
292
+ | Prop | Type | Default | Description |
293
+ | --- | --- | --- | --- |
294
+ | `marker` | `"disc" \| "decimal" \| "none"` | `"disc"` | Marker style; `decimal` renders `<ol>`. |
295
+ | `spacing` | `SpacingToken` | `"space-2"` | Vertical spacing between items (margin for disc/decimal, gap for none). |
296
+ | `indent` | `SpacingToken \| string` | — | Left indent (e.g. `"space-6"`, `"space-list-indent"` for 27px, or `"27px"`). |
297
+ | `itemPadding` | `"default" \| "none"` | `"default"` | Item left padding; `"none"` for body-copy lists. |
298
+
299
+ ### Card
300
+
301
+ Card with optional image, title, description, and action. Aligned with Figma (web · library): media cards use secondary background, fluid padding/gaps, title-md/text-md typography, and 48px action button with rounded-rad-md.
302
+
303
+ ```tsx
304
+ <Card type="text-only" title="Title" description="Description." label="Action" onLabelClick={() => {}} />
305
+
306
+ <Card
307
+ type="media-horizontal"
308
+ imageSrc="/img.jpg"
309
+ imageAlt="Product"
310
+ title="Product"
311
+ description="Description."
312
+ label="View"
313
+ labelHref="/product"
314
+ />
315
+
316
+ <Card
317
+ type="media-horizontal"
318
+ imageSrc="/img.jpg"
319
+ imageAlt="Product"
320
+ message="Special Offer"
321
+ title="Product"
322
+ description="Description."
323
+ label="Get started"
324
+ />
325
+ ```
326
+
327
+ **Props:** `type` (text-only | media-horizontal | media-vertical), `imageSrc`, `imageAlt`, `message` (overlay on image), `title`, `description`, `label`, `onLabelClick`, `labelHref`, `linkAs`.
328
+
329
+ ### Breadcrumb
330
+
331
+ Breadcrumb navigation with slash or chevron separator. Optional truncation for long paths (first … second-to-last / current).
332
+
333
+ ```tsx
334
+ <Breadcrumb
335
+ items={[
336
+ { label: 'Home', href: '/' },
337
+ { label: 'Products', href: '/products' },
338
+ { label: 'Detail' },
339
+ ]}
340
+ />
341
+
342
+ <Breadcrumb items={manyItems} truncate separator="slash" />
343
+ ```
344
+
345
+ **Props:** `items` (array of `{ label, href? }`), `truncate`, `linkVariant`, `separator` (`"slash"` \| `"chevron"`).
346
+
263
347
  ### Grid
264
348
 
265
349
  CSS Grid layout with responsive column counts.
@@ -6,10 +6,9 @@ type SystemPropsWithoutSpacing = Omit<SystemProps, 'p' | 'px' | 'py' | 'pt' | 'p
6
6
  /** Omit display so LayoutProps (class-based) takes precedence over SystemProps (inline). */
7
7
  type SystemPropsWithoutSpacingAndDisplay = Omit<SystemPropsWithoutSpacing, 'display'>;
8
8
  /**
9
- * Props for the Box component. Extends HTML attributes, system props
10
- * (dimensions, position, flex), SpacingProps, and LayoutProps.
9
+ * Props for the Box component using complete polymorphic typings.
11
10
  */
12
- export interface BoxProps extends Omit<React.HTMLAttributes<HTMLElement>, 'display'>, SystemPropsWithoutSpacingAndDisplay, SpacingProps, LayoutProps {
11
+ export type BoxProps<C extends React.ElementType> = {
13
12
  /**
14
13
  * The HTML element or React component to render as the root element.
15
14
  * Accepts any valid HTML tag string ('div', 'section', 'article',
@@ -20,37 +19,16 @@ export interface BoxProps extends Omit<React.HTMLAttributes<HTMLElement>, 'displ
20
19
  * design system layout control.
21
20
  * @default 'div'
22
21
  */
23
- as?: React.ElementType;
24
- /** Forwarded ref for the root element. @default undefined */
25
- ref?: React.Ref<HTMLElement>;
22
+ as?: C;
26
23
  /** Child content. @default undefined */
27
24
  children?: React.ReactNode;
25
+ } & Omit<React.ComponentPropsWithoutRef<C>, keyof SystemPropsWithoutSpacingAndDisplay | keyof SpacingProps | keyof LayoutProps | 'as' | 'children'> & SystemPropsWithoutSpacingAndDisplay & SpacingProps & LayoutProps;
26
+ export interface BoxComponent {
27
+ <C extends React.ElementType = 'div'>(props: BoxProps<C> & {
28
+ ref?: React.ComponentPropsWithRef<C>['ref'];
29
+ }): React.ReactElement | null;
30
+ displayName?: string;
28
31
  }
29
- /**
30
- * General-purpose layout primitive. Renders any HTML element via the
31
- * as prop (default: div). Use Box when you need box-model control
32
- * (spacing, sizing) or display/alignment props on an arbitrary element.
33
- * Prefer Stack for linear sequences, Grid for multi-column layouts,
34
- * and Container for page-width constraints.
35
- *
36
- * @example
37
- * // Basic spacing
38
- * <Box p="space-4" mt="space-8">Content</Box>
39
- *
40
- * @example
41
- * // Inline flex wrapper
42
- * <Box as="span" display="inline-flex" alignItems="center">
43
- * <Icon />
44
- * Label
45
- * </Box>
46
- *
47
- * @example
48
- * // Semantic section element
49
- * <Box as="section" py="fluid-8-16">Content</Box>
50
- */
51
- export declare function Box({ as: Tag, children, className, style, ref, ...allRest }: BoxProps): import("react/jsx-runtime").JSX.Element;
52
- export declare namespace Box {
53
- var displayName: string;
54
- }
32
+ export declare const Box: BoxComponent;
55
33
  export type { ResponsiveValue };
56
34
  //# sourceMappingURL=Box.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Box.d.ts","sourceRoot":"","sources":["../../../src/components/Box/Box.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,eAAe,EACrB,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAItD,KAAK,yBAAyB,GAAG,IAAI,CACnC,WAAW,EACX,GAAG,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAC9F,CAAC;AAEF,4FAA4F;AAC5F,KAAK,mCAAmC,GAAG,IAAI,CAAC,yBAAyB,EAAE,SAAS,CAAC,CAAC;AAEtF;;;GAGG;AACH,MAAM,WAAW,QACf,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE,SAAS,CAAC,EACxD,mCAAmC,EACnC,YAAY,EACZ,WAAW;IACb;;;;;;;;;OASG;IACH,EAAE,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC;IACvB,6DAA6D;IAC7D,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC7B,wCAAwC;IACxC,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,GAAG,CAAC,EAClB,EAAE,EAAE,GAAW,EACf,QAAQ,EACR,SAAc,EACd,KAAK,EACL,GAAG,EACH,GAAG,OAAO,EACX,EAAE,QAAQ,2CAyCV;yBAhDe,GAAG;;;AAoDnB,YAAY,EAAE,eAAe,EAAE,CAAC"}
1
+ {"version":3,"file":"Box.d.ts","sourceRoot":"","sources":["../../../src/components/Box/Box.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,eAAe,EACrB,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAItD,KAAK,yBAAyB,GAAG,IAAI,CACnC,WAAW,EACX,GAAG,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAC9F,CAAC;AAEF,4FAA4F;AAC5F,KAAK,mCAAmC,GAAG,IAAI,CAAC,yBAAyB,EAAE,SAAS,CAAC,CAAC;AAEtF;;GAEG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,KAAK,CAAC,WAAW,IAAI;IAClD;;;;;;;;;OASG;IACH,EAAE,CAAC,EAAE,CAAC,CAAC;IACP,wCAAwC;IACxC,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B,GAAG,IAAI,CACN,KAAK,CAAC,wBAAwB,CAAC,CAAC,CAAC,EAC/B,MAAM,mCAAmC,GACzC,MAAM,YAAY,GAClB,MAAM,WAAW,GACjB,IAAI,GACJ,UAAU,CACb,GACC,mCAAmC,GACnC,YAAY,GACZ,WAAW,CAAC;AAEd,MAAM,WAAW,YAAY;IAC3B,CAAC,CAAC,SAAS,KAAK,CAAC,WAAW,GAAG,KAAK,EAClC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG;QAAE,GAAG,CAAC,EAAE,KAAK,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;KAAE,GACnE,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAiDD,eAAO,MAAM,GAAG,EAAmD,YAAY,CAAC;AAIhF,YAAY,EAAE,eAAe,EAAE,CAAC"}
@@ -3,14 +3,8 @@ import React from "react";
3
3
  import { stripSystemProps, renderSystemProps } from "../../utils/responsive-props.js";
4
4
  import { SPACING_PROP_KEYS, resolveSpacing } from "../../utils/spacing.js";
5
5
  import { LAYOUT_PROP_KEYS, resolveLayout } from "../../utils/layoutClasses.js";
6
- function Box({
7
- as: Tag = "div",
8
- children,
9
- className = "",
10
- style,
11
- ref,
12
- ...allRest
13
- }) {
6
+ const BoxInner = (props, ref) => {
7
+ const { as: Tag = "div", children, className = "", style, ...allRest } = props;
14
8
  const systemPropsOnly = { ...allRest };
15
9
  for (const key of SPACING_PROP_KEYS) {
16
10
  delete systemPropsOnly[key];
@@ -23,10 +17,11 @@ function Box({
23
17
  for (const key of LAYOUT_PROP_KEYS) {
24
18
  delete domRest[key];
25
19
  }
26
- const { style: systemStyle, styleTag, dataId } = renderSystemProps(
27
- systemPropsOnly,
28
- style
29
- );
20
+ const {
21
+ style: systemStyle,
22
+ styleTag,
23
+ dataId
24
+ } = renderSystemProps(systemPropsOnly, style);
30
25
  const spacingClasses = resolveSpacing(allRest);
31
26
  const layoutClasses = resolveLayout(allRest);
32
27
  const mergedClassName = [layoutClasses, spacingClasses, className].filter(Boolean).join(" ");
@@ -43,7 +38,8 @@ function Box({
43
38
  styleTag,
44
39
  React.createElement(Tag, elementProps, children)
45
40
  ] });
46
- }
41
+ };
42
+ const Box = React.forwardRef(BoxInner);
47
43
  Box.displayName = "Box";
48
44
  export {
49
45
  Box
@@ -1 +1 @@
1
- {"version":3,"file":"Box.js","sources":["../../../src/components/Box/Box.tsx"],"sourcesContent":["import React from 'react';\nimport {\n renderSystemProps,\n stripSystemProps,\n type SystemProps,\n type ResponsiveValue,\n} from '../../utils/responsive-props';\nimport type { SpacingProps } from '../../types/spacing';\nimport type { LayoutProps } from '../../types/layout';\nimport { resolveSpacing, SPACING_PROP_KEYS } from '../../utils/spacing';\nimport { resolveLayout, LAYOUT_PROP_KEYS } from '../../utils/layoutClasses';\n\ntype SystemPropsWithoutSpacing = Omit<\n SystemProps,\n 'p' | 'px' | 'py' | 'pt' | 'pb' | 'pl' | 'pr' | 'm' | 'mx' | 'my' | 'mt' | 'mb' | 'ml' | 'mr'\n>;\n\n/** Omit display so LayoutProps (class-based) takes precedence over SystemProps (inline). */\ntype SystemPropsWithoutSpacingAndDisplay = Omit<SystemPropsWithoutSpacing, 'display'>;\n\n/**\n * Props for the Box component. Extends HTML attributes, system props\n * (dimensions, position, flex), SpacingProps, and LayoutProps.\n */\nexport interface BoxProps\n extends Omit<React.HTMLAttributes<HTMLElement>, 'display'>,\n SystemPropsWithoutSpacingAndDisplay,\n SpacingProps,\n LayoutProps {\n /**\n * The HTML element or React component to render as the root element.\n * Accepts any valid HTML tag string ('div', 'section', 'article',\n * 'span', 'main', 'aside', 'header', 'footer', 'nav', 'ul', 'ol' etc.)\n * or a React component type.\n *\n * Use this to maintain correct semantic HTML without sacrificing\n * design system layout control.\n * @default 'div'\n */\n as?: React.ElementType;\n /** Forwarded ref for the root element. @default undefined */\n ref?: React.Ref<HTMLElement>;\n /** Child content. @default undefined */\n children?: React.ReactNode;\n}\n\n/**\n * General-purpose layout primitive. Renders any HTML element via the\n * as prop (default: div). Use Box when you need box-model control\n * (spacing, sizing) or display/alignment props on an arbitrary element.\n * Prefer Stack for linear sequences, Grid for multi-column layouts,\n * and Container for page-width constraints.\n *\n * @example\n * // Basic spacing\n * <Box p=\"space-4\" mt=\"space-8\">Content</Box>\n *\n * @example\n * // Inline flex wrapper\n * <Box as=\"span\" display=\"inline-flex\" alignItems=\"center\">\n * <Icon />\n * Label\n * </Box>\n *\n * @example\n * // Semantic section element\n * <Box as=\"section\" py=\"fluid-8-16\">Content</Box>\n */\nexport function Box({\n as: Tag = 'div',\n children,\n className = '',\n style,\n ref,\n ...allRest\n}: BoxProps) {\n const systemPropsOnly = { ...allRest };\n for (const key of SPACING_PROP_KEYS) {\n delete (systemPropsOnly as Record<string, unknown>)[key];\n }\n for (const key of LAYOUT_PROP_KEYS) {\n delete (systemPropsOnly as Record<string, unknown>)[key];\n }\n const rest = stripSystemProps(allRest);\n const domRest = { ...rest };\n for (const key of LAYOUT_PROP_KEYS) {\n delete (domRest as Record<string, unknown>)[key];\n }\n const { style: systemStyle, styleTag, dataId } = renderSystemProps(\n systemPropsOnly as SystemPropsWithoutSpacingAndDisplay,\n style,\n );\n\n const spacingClasses = resolveSpacing(allRest);\n const layoutClasses = resolveLayout(allRest as LayoutProps);\n const mergedClassName = [layoutClasses, spacingClasses, className]\n .filter(Boolean)\n .join(' ');\n\n const elementProps: Record<string, unknown> = {\n ref,\n className: mergedClassName || undefined,\n style: systemStyle,\n ...domRest,\n };\n\n if (dataId) {\n elementProps['data-jds'] = dataId;\n }\n\n return (\n <>\n {styleTag}\n {React.createElement(Tag, elementProps, children)}\n </>\n );\n}\n\nBox.displayName = 'Box';\n\nexport type { ResponsiveValue };\n"],"names":[],"mappings":";;;;;AAoEO,SAAS,IAAI;AAAA,EAClB,IAAI,MAAM;AAAA,EACV;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAa;AACX,QAAM,kBAAkB,EAAE,GAAG,QAAA;AAC7B,aAAW,OAAO,mBAAmB;AACnC,WAAQ,gBAA4C,GAAG;AAAA,EACzD;AACA,aAAW,OAAO,kBAAkB;AAClC,WAAQ,gBAA4C,GAAG;AAAA,EACzD;AACA,QAAM,OAAO,iBAAiB,OAAO;AACrC,QAAM,UAAU,EAAE,GAAG,KAAA;AACrB,aAAW,OAAO,kBAAkB;AAClC,WAAQ,QAAoC,GAAG;AAAA,EACjD;AACA,QAAM,EAAE,OAAO,aAAa,UAAU,WAAW;AAAA,IAC/C;AAAA,IACA;AAAA,EAAA;AAGF,QAAM,iBAAiB,eAAe,OAAO;AAC7C,QAAM,gBAAgB,cAAc,OAAsB;AAC1D,QAAM,kBAAkB,CAAC,eAAe,gBAAgB,SAAS,EAC9D,OAAO,OAAO,EACd,KAAK,GAAG;AAEX,QAAM,eAAwC;AAAA,IAC5C;AAAA,IACA,WAAW,mBAAmB;AAAA,IAC9B,OAAO;AAAA,IACP,GAAG;AAAA,EAAA;AAGL,MAAI,QAAQ;AACV,iBAAa,UAAU,IAAI;AAAA,EAC7B;AAEA,SACE,qBAAA,UAAA,EACG,UAAA;AAAA,IAAA;AAAA,IACA,MAAM,cAAc,KAAK,cAAc,QAAQ;AAAA,EAAA,GAClD;AAEJ;AAEA,IAAI,cAAc;"}
1
+ {"version":3,"file":"Box.js","sources":["../../../src/components/Box/Box.tsx"],"sourcesContent":["import React from 'react';\nimport {\n renderSystemProps,\n stripSystemProps,\n type SystemProps,\n type ResponsiveValue,\n} from '../../utils/responsive-props';\nimport type { SpacingProps } from '../../types/spacing';\nimport type { LayoutProps } from '../../types/layout';\nimport { resolveSpacing, SPACING_PROP_KEYS } from '../../utils/spacing';\nimport { resolveLayout, LAYOUT_PROP_KEYS } from '../../utils/layoutClasses';\n\ntype SystemPropsWithoutSpacing = Omit<\n SystemProps,\n 'p' | 'px' | 'py' | 'pt' | 'pb' | 'pl' | 'pr' | 'm' | 'mx' | 'my' | 'mt' | 'mb' | 'ml' | 'mr'\n>;\n\n/** Omit display so LayoutProps (class-based) takes precedence over SystemProps (inline). */\ntype SystemPropsWithoutSpacingAndDisplay = Omit<SystemPropsWithoutSpacing, 'display'>;\n\n/**\n * Props for the Box component using complete polymorphic typings.\n */\nexport type BoxProps<C extends React.ElementType> = {\n /**\n * The HTML element or React component to render as the root element.\n * Accepts any valid HTML tag string ('div', 'section', 'article',\n * 'span', 'main', 'aside', 'header', 'footer', 'nav', 'ul', 'ol' etc.)\n * or a React component type.\n *\n * Use this to maintain correct semantic HTML without sacrificing\n * design system layout control.\n * @default 'div'\n */\n as?: C;\n /** Child content. @default undefined */\n children?: React.ReactNode;\n} & Omit<\n React.ComponentPropsWithoutRef<C>,\n | keyof SystemPropsWithoutSpacingAndDisplay\n | keyof SpacingProps\n | keyof LayoutProps\n | 'as'\n | 'children'\n> &\n SystemPropsWithoutSpacingAndDisplay &\n SpacingProps &\n LayoutProps;\n\nexport interface BoxComponent {\n <C extends React.ElementType = 'div'>(\n props: BoxProps<C> & { ref?: React.ComponentPropsWithRef<C>['ref'] },\n ): React.ReactElement | null;\n displayName?: string;\n}\n\nconst BoxInner = <C extends React.ElementType = 'div'>(\n props: BoxProps<C>,\n ref: React.ComponentPropsWithRef<C>['ref'],\n) => {\n const { as: Tag = 'div', children, className = '', style, ...allRest } = props as any;\n\n const systemPropsOnly = { ...allRest };\n for (const key of SPACING_PROP_KEYS) {\n delete (systemPropsOnly as Record<string, unknown>)[key];\n }\n for (const key of LAYOUT_PROP_KEYS) {\n delete (systemPropsOnly as Record<string, unknown>)[key];\n }\n const rest = stripSystemProps(allRest);\n const domRest = { ...rest };\n for (const key of LAYOUT_PROP_KEYS) {\n delete (domRest as Record<string, unknown>)[key];\n }\n const {\n style: systemStyle,\n styleTag,\n dataId,\n } = renderSystemProps(systemPropsOnly as SystemPropsWithoutSpacingAndDisplay, style);\n\n const spacingClasses = resolveSpacing(allRest);\n const layoutClasses = resolveLayout(allRest as LayoutProps);\n const mergedClassName = [layoutClasses, spacingClasses, className].filter(Boolean).join(' ');\n\n const elementProps: Record<string, unknown> = {\n ref,\n className: mergedClassName || undefined,\n style: systemStyle,\n ...domRest,\n };\n\n if (dataId) {\n elementProps['data-jds'] = dataId;\n }\n\n return (\n <>\n {styleTag}\n {React.createElement(Tag, elementProps, children)}\n </>\n );\n};\n\nexport const Box = React.forwardRef(BoxInner as any) as unknown as BoxComponent;\n\nBox.displayName = 'Box';\n\nexport type { ResponsiveValue };\n"],"names":[],"mappings":";;;;;AAwDA,MAAM,WAAW,CACf,OACA,QACG;AACH,QAAM,EAAE,IAAI,MAAM,OAAO,UAAU,YAAY,IAAI,OAAO,GAAG,QAAA,IAAY;AAEzE,QAAM,kBAAkB,EAAE,GAAG,QAAA;AAC7B,aAAW,OAAO,mBAAmB;AACnC,WAAQ,gBAA4C,GAAG;AAAA,EACzD;AACA,aAAW,OAAO,kBAAkB;AAClC,WAAQ,gBAA4C,GAAG;AAAA,EACzD;AACA,QAAM,OAAO,iBAAiB,OAAO;AACrC,QAAM,UAAU,EAAE,GAAG,KAAA;AACrB,aAAW,OAAO,kBAAkB;AAClC,WAAQ,QAAoC,GAAG;AAAA,EACjD;AACA,QAAM;AAAA,IACJ,OAAO;AAAA,IACP;AAAA,IACA;AAAA,EAAA,IACE,kBAAkB,iBAAwD,KAAK;AAEnF,QAAM,iBAAiB,eAAe,OAAO;AAC7C,QAAM,gBAAgB,cAAc,OAAsB;AAC1D,QAAM,kBAAkB,CAAC,eAAe,gBAAgB,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAE3F,QAAM,eAAwC;AAAA,IAC5C;AAAA,IACA,WAAW,mBAAmB;AAAA,IAC9B,OAAO;AAAA,IACP,GAAG;AAAA,EAAA;AAGL,MAAI,QAAQ;AACV,iBAAa,UAAU,IAAI;AAAA,EAC7B;AAEA,SACE,qBAAA,UAAA,EACG,UAAA;AAAA,IAAA;AAAA,IACA,MAAM,cAAc,KAAK,cAAc,QAAQ;AAAA,EAAA,GAClD;AAEJ;AAEO,MAAM,MAAM,MAAM,WAAW,QAAe;AAEnD,IAAI,cAAc;"}
@@ -1 +1 @@
1
- {"version":3,"file":"Card.d.ts","sourceRoot":"","sources":["../../../src/components/Card/Card.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAO1B;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,WAAW,GAAG,kBAAkB,GAAG,gBAAgB,CAAC;AAE3E;;GAEG;AACH,MAAM,WAAW,SAAU,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IACrE,8FAA8F;IAC9F,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,qGAAqG;IACrG,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0CAA0C;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qFAAqF;IACrF,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+FAA+F;IAC/F,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,8EAA8E;IAC9E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC;IAC3B,yDAAyD;IACzD,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;CACjC;AAmFD;;;;;;;;;;GAUG;AACH,wBAAgB,IAAI,CAAC,EACnB,IAAkB,EAClB,QAAQ,EACR,QAAa,EACb,UAAgB,EAChB,WAAiB,EACjB,YAAY,EACZ,gBAAgB,EAChB,OAAO,EACP,KAAK,EACL,WAAW,EACX,KAAK,EACL,YAAY,EACZ,SAAS,EACT,MAAM,EACN,SAAc,EACd,GAAG,EACH,GAAG,IAAI,EACR,EAAE,SAAS,2CAuCX"}
1
+ {"version":3,"file":"Card.d.ts","sourceRoot":"","sources":["../../../src/components/Card/Card.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAO1B;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,WAAW,GAAG,kBAAkB,GAAG,gBAAgB,CAAC;AAE3E;;GAEG;AACH,MAAM,WAAW,SAAU,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IACrE,8FAA8F;IAC9F,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,qGAAqG;IACrG,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0CAA0C;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qFAAqF;IACrF,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+FAA+F;IAC/F,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,8EAA8E;IAC9E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC;IAC3B,yDAAyD;IACzD,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;CACjC;AAmFD;;;;;;;;;;GAUG;AACH,wBAAgB,IAAI,CAAC,EACnB,IAAkB,EAClB,QAAQ,EACR,QAAa,EACb,UAAgB,EAChB,WAAiB,EACjB,YAAY,EACZ,gBAAgB,EAChB,OAAO,EACP,KAAK,EACL,WAAW,EACX,KAAK,EACL,YAAY,EACZ,SAAS,EACT,MAAM,EACN,SAAc,EACd,GAAG,EACH,GAAG,IAAI,EACR,EAAE,SAAS,2CAgDX"}
@@ -24,7 +24,7 @@ function CardImage({
24
24
  className: "h-full w-full object-cover"
25
25
  }
26
26
  ),
27
- message && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-green-950/50", children: /* @__PURE__ */ jsx("span", { className: "text-title-md text-neutral-0 text-center px-space-4", children: message }) })
27
+ message && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-fill-bg-dimmer", children: /* @__PURE__ */ jsx("span", { className: "text-title-lg text-text-primary-inverse text-center leading-[1.1] px-space-fluid-4-8", children: message }) })
28
28
  ] });
29
29
  }
30
30
  function CardAction({
@@ -33,7 +33,7 @@ function CardAction({
33
33
  href,
34
34
  linkAs
35
35
  }) {
36
- const classes = "inline-flex items-center justify-center rounded-rad-sm px-space-4 py-space-2 text-btn-small bg-fill-action-primary text-text-action-primary hover:bg-fill-action-primary-hover active:bg-fill-action-primary-active transition-colors";
36
+ const classes = "inline-flex items-center justify-center h-12 min-h-12 rounded-rad-md px-space-5 text-btn-reg bg-fill-action-primary text-text-action-primary hover:bg-fill-action-primary-hover active:bg-fill-action-primary-active transition-colors";
37
37
  if (href) {
38
38
  const LinkComponent = linkAs || "a";
39
39
  return /* @__PURE__ */ jsx(LinkComponent, { href, className: classes, children: label });
@@ -61,36 +61,37 @@ function Card({
61
61
  }) {
62
62
  const isMedia = type !== "text-only";
63
63
  const ratio = type === "media-vertical" ? "vertical" : "horizontal";
64
- return /* @__PURE__ */ jsxs(
65
- "div",
66
- {
67
- ref,
68
- className: `flex flex-col rounded-rad-md border border-border-light bg-fill-bg-primary overflow-hidden ${className}`,
69
- ...rest,
70
- children: [
71
- isMedia && imageSrc && /* @__PURE__ */ jsx(
72
- CardImage,
73
- {
74
- src: imageSrc,
75
- alt: imageAlt,
76
- ratio,
77
- message,
78
- width: imageWidth,
79
- height: imageHeight,
80
- quality: imageQuality,
81
- unoptimized: imageUnoptimized
82
- }
83
- ),
84
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-space-3 p-space-4", children: [
85
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-space-1", children: [
86
- /* @__PURE__ */ jsx("h3", { className: "text-title-sm text-text-primary", children: title }),
87
- description && /* @__PURE__ */ jsx("p", { className: "text-text-sm text-text-secondary", children: description })
88
- ] }),
89
- label && /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(CardAction, { label, onClick: onLabelClick, href: labelHref, linkAs }) })
90
- ] })
91
- ]
92
- }
93
- );
64
+ const rootClasses = [
65
+ "flex flex-col rounded-rad-md overflow-hidden",
66
+ isMedia ? "bg-fill-bg-secondary pb-space-fluid-4-8 gap-space-fluid-4-6" : "bg-fill-bg-primary border border-border-light",
67
+ className
68
+ ].filter(Boolean).join(" ");
69
+ const contentClasses = [
70
+ "flex flex-col text-text-primary",
71
+ isMedia ? "gap-space-fluid-4-6 px-space-fluid-4-8" : "gap-space-3 p-space-4"
72
+ ].filter(Boolean).join(" ");
73
+ return /* @__PURE__ */ jsxs("div", { ref, className: rootClasses, ...rest, children: [
74
+ isMedia && imageSrc && /* @__PURE__ */ jsx(
75
+ CardImage,
76
+ {
77
+ src: imageSrc,
78
+ alt: imageAlt,
79
+ ratio,
80
+ message,
81
+ width: imageWidth,
82
+ height: imageHeight,
83
+ quality: imageQuality,
84
+ unoptimized: imageUnoptimized
85
+ }
86
+ ),
87
+ /* @__PURE__ */ jsxs("div", { className: contentClasses, children: [
88
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-space-1", children: [
89
+ /* @__PURE__ */ jsx("h3", { className: "text-title-md leading-[1.2] tracking-[0.19px]", children: title }),
90
+ description && /* @__PURE__ */ jsx("p", { className: "text-text-md leading-[1.4]", children: description })
91
+ ] }),
92
+ label && /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(CardAction, { label, onClick: onLabelClick, href: labelHref, linkAs }) })
93
+ ] })
94
+ ] });
94
95
  }
95
96
  export {
96
97
  Card
@@ -1 +1 @@
1
- {"version":3,"file":"Card.js","sources":["../../../src/components/Card/Card.tsx"],"sourcesContent":["import React from 'react';\nimport Image from 'next/image';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Card layout type. 'text-only' — no image; 'media-horizontal' — image above content, video aspect;\n * 'media-vertical' — image above content, 4:3 aspect.\n */\nexport type CardType = 'text-only' | 'media-horizontal' | 'media-vertical';\n\n/**\n * Props for the Card component. Title is required; image and action are optional.\n */\nexport interface CardProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Layout: text-only, or media with horizontal/vertical image aspect. @default 'text-only' */\n type?: CardType;\n /** Image source URL. Required when type is media-horizontal or media-vertical. @default undefined */\n imageSrc?: string;\n /** Image alt text for accessibility. @default '' */\n imageAlt?: string;\n /** Width for next/image. @default 600 */\n imageWidth?: number;\n /** Height for next/image. @default 400 */\n imageHeight?: number;\n /** Next.js image quality (1-100). @default undefined */\n imageQuality?: number;\n /** Bypass Next.js image optimization. Useful for small sharp PNGs. @default false */\n imageUnoptimized?: boolean;\n /** Overlay message displayed centered on the image. @default undefined */\n message?: string;\n /** Card title (required). Rendered as heading. */\n title: string;\n /** Card description text below the title. @default undefined */\n description?: string;\n /** Label for the action button or link. @default undefined */\n label?: string;\n /** Called when the action button is clicked (when labelHref is not set). @default undefined */\n onLabelClick?: () => void;\n /** When set, the action renders as a link to this href. @default undefined */\n labelHref?: string;\n /** Component to render the action link as (e.g. next/link). @default 'a' */\n linkAs?: React.ElementType;\n /** Forwarded ref for the root div. @default undefined */\n ref?: React.Ref<HTMLDivElement>;\n}\n\n// ---------------------------------------------------------------------------\n// Sub-components\n// ---------------------------------------------------------------------------\n\nfunction CardImage({\n src,\n alt,\n ratio,\n message,\n width,\n height,\n quality,\n unoptimized,\n}: {\n src: string;\n alt: string;\n ratio: 'horizontal' | 'vertical';\n message?: string;\n width: number;\n height: number;\n quality?: number;\n unoptimized?: boolean;\n}) {\n const aspectClass = ratio === 'horizontal' ? 'aspect-video' : 'aspect-[4/3]';\n\n return (\n <div className={`relative w-full overflow-hidden rounded-t-rad-md ${aspectClass}`}>\n <Image\n src={src}\n alt={alt}\n width={width}\n height={height}\n quality={quality}\n unoptimized={unoptimized}\n className=\"h-full w-full object-cover\"\n />\n {message && (\n <div className=\"absolute inset-0 flex items-center justify-center bg-green-950/50\">\n <span className=\"text-title-md text-neutral-0 text-center px-space-4\">\n {message}\n </span>\n </div>\n )}\n </div>\n );\n}\n\nfunction CardAction({\n label,\n onClick,\n href,\n linkAs,\n}: {\n label: string;\n onClick?: () => void;\n href?: string;\n linkAs?: React.ElementType;\n}) {\n const classes =\n 'inline-flex items-center justify-center rounded-rad-sm px-space-4 py-space-2 text-btn-small bg-fill-action-primary text-text-action-primary hover:bg-fill-action-primary-hover active:bg-fill-action-primary-active transition-colors';\n\n if (href) {\n const LinkComponent = linkAs || 'a';\n return (\n <LinkComponent href={href} className={classes}>\n {label}\n </LinkComponent>\n );\n }\n\n return (\n <button type=\"button\" onClick={onClick} className={classes}>\n {label}\n </button>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Card\n// ---------------------------------------------------------------------------\n\n/**\n * Card with optional image, title, description, and action. Renders a bordered container\n * with design-token styling. Use for product cards, feature highlights, or content blocks.\n * For image-only or custom layouts use Box or a custom component.\n *\n * @example\n * <Card title=\"Feature\" description=\"Description here.\" type=\"text-only\" />\n *\n * @example\n * <Card type=\"media-horizontal\" imageSrc=\"/img.jpg\" imageAlt=\"Product\" title=\"Product\" label=\"View\" labelHref=\"/product\" />\n */\nexport function Card({\n type = 'text-only',\n imageSrc,\n imageAlt = '',\n imageWidth = 600,\n imageHeight = 400,\n imageQuality,\n imageUnoptimized,\n message,\n title,\n description,\n label,\n onLabelClick,\n labelHref,\n linkAs,\n className = '',\n ref,\n ...rest\n}: CardProps) {\n const isMedia = type !== 'text-only';\n const ratio = type === 'media-vertical' ? 'vertical' : 'horizontal';\n\n return (\n <div\n ref={ref}\n className={`flex flex-col rounded-rad-md border border-border-light bg-fill-bg-primary overflow-hidden ${className}`}\n {...rest}\n >\n {isMedia && imageSrc && (\n <CardImage\n src={imageSrc}\n alt={imageAlt}\n ratio={ratio}\n message={message}\n width={imageWidth}\n height={imageHeight}\n quality={imageQuality}\n unoptimized={imageUnoptimized}\n />\n )}\n\n <div className=\"flex flex-col gap-space-3 p-space-4\">\n <div className=\"flex flex-col gap-space-1\">\n <h3 className=\"text-title-sm text-text-primary\">{title}</h3>\n {description && (\n <p className=\"text-text-sm text-text-secondary\">{description}</p>\n )}\n </div>\n\n {label && (\n <div>\n <CardAction label={label} onClick={onLabelClick} href={labelHref} linkAs={linkAs} />\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"names":[],"mappings":";;AAqDA,SAAS,UAAU;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GASG;AACD,QAAM,cAAc,UAAU,eAAe,iBAAiB;AAE9D,SACE,qBAAC,OAAA,EAAI,WAAW,oDAAoD,WAAW,IAC7E,UAAA;AAAA,IAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,WAAU;AAAA,MAAA;AAAA,IAAA;AAAA,IAEX,WACC,oBAAC,OAAA,EAAI,WAAU,qEACb,8BAAC,QAAA,EAAK,WAAU,uDACb,UAAA,QAAA,CACH,EAAA,CACF;AAAA,EAAA,GAEJ;AAEJ;AAEA,SAAS,WAAW;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,QAAM,UACJ;AAEF,MAAI,MAAM;AACR,UAAM,gBAAgB,UAAU;AAChC,WACE,oBAAC,eAAA,EAAc,MAAY,WAAW,SACnC,UAAA,OACH;AAAA,EAEJ;AAEA,6BACG,UAAA,EAAO,MAAK,UAAS,SAAkB,WAAW,SAChD,UAAA,OACH;AAEJ;AAiBO,SAAS,KAAK;AAAA,EACnB,OAAO;AAAA,EACP;AAAA,EACA,WAAW;AAAA,EACX,aAAa;AAAA,EACb,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA,GAAG;AACL,GAAc;AACZ,QAAM,UAAU,SAAS;AACzB,QAAM,QAAQ,SAAS,mBAAmB,aAAa;AAEvD,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACA,WAAW,8FAA8F,SAAS;AAAA,MACjH,GAAG;AAAA,MAEH,UAAA;AAAA,QAAA,WAAW,YACV;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,KAAK;AAAA,YACL,KAAK;AAAA,YACL;AAAA,YACA;AAAA,YACA,OAAO;AAAA,YACP,QAAQ;AAAA,YACR,SAAS;AAAA,YACT,aAAa;AAAA,UAAA;AAAA,QAAA;AAAA,QAIjB,qBAAC,OAAA,EAAI,WAAU,uCACb,UAAA;AAAA,UAAA,qBAAC,OAAA,EAAI,WAAU,6BACb,UAAA;AAAA,YAAA,oBAAC,MAAA,EAAG,WAAU,mCAAmC,UAAA,OAAM;AAAA,YACtD,eACC,oBAAC,KAAA,EAAE,WAAU,oCAAoC,UAAA,YAAA,CAAY;AAAA,UAAA,GAEjE;AAAA,UAEC,SACC,oBAAC,OAAA,EACC,UAAA,oBAAC,YAAA,EAAW,OAAc,SAAS,cAAc,MAAM,WAAW,OAAA,CAAgB,EAAA,CACpF;AAAA,QAAA,EAAA,CAEJ;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGN;"}
1
+ {"version":3,"file":"Card.js","sources":["../../../src/components/Card/Card.tsx"],"sourcesContent":["import React from 'react';\nimport Image from 'next/image';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Card layout type. 'text-only' — no image; 'media-horizontal' — image above content, video aspect;\n * 'media-vertical' — image above content, 4:3 aspect.\n */\nexport type CardType = 'text-only' | 'media-horizontal' | 'media-vertical';\n\n/**\n * Props for the Card component. Title is required; image and action are optional.\n */\nexport interface CardProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Layout: text-only, or media with horizontal/vertical image aspect. @default 'text-only' */\n type?: CardType;\n /** Image source URL. Required when type is media-horizontal or media-vertical. @default undefined */\n imageSrc?: string;\n /** Image alt text for accessibility. @default '' */\n imageAlt?: string;\n /** Width for next/image. @default 600 */\n imageWidth?: number;\n /** Height for next/image. @default 400 */\n imageHeight?: number;\n /** Next.js image quality (1-100). @default undefined */\n imageQuality?: number;\n /** Bypass Next.js image optimization. Useful for small sharp PNGs. @default false */\n imageUnoptimized?: boolean;\n /** Overlay message displayed centered on the image. @default undefined */\n message?: string;\n /** Card title (required). Rendered as heading. */\n title: string;\n /** Card description text below the title. @default undefined */\n description?: string;\n /** Label for the action button or link. @default undefined */\n label?: string;\n /** Called when the action button is clicked (when labelHref is not set). @default undefined */\n onLabelClick?: () => void;\n /** When set, the action renders as a link to this href. @default undefined */\n labelHref?: string;\n /** Component to render the action link as (e.g. next/link). @default 'a' */\n linkAs?: React.ElementType;\n /** Forwarded ref for the root div. @default undefined */\n ref?: React.Ref<HTMLDivElement>;\n}\n\n// ---------------------------------------------------------------------------\n// Sub-components\n// ---------------------------------------------------------------------------\n\nfunction CardImage({\n src,\n alt,\n ratio,\n message,\n width,\n height,\n quality,\n unoptimized,\n}: {\n src: string;\n alt: string;\n ratio: 'horizontal' | 'vertical';\n message?: string;\n width: number;\n height: number;\n quality?: number;\n unoptimized?: boolean;\n}) {\n const aspectClass = ratio === 'horizontal' ? 'aspect-video' : 'aspect-[4/3]';\n\n return (\n <div className={`relative w-full overflow-hidden rounded-t-rad-md ${aspectClass}`}>\n <Image\n src={src}\n alt={alt}\n width={width}\n height={height}\n quality={quality}\n unoptimized={unoptimized}\n className=\"h-full w-full object-cover\"\n />\n {message && (\n <div className=\"absolute inset-0 flex items-center justify-center bg-fill-bg-dimmer\">\n <span className=\"text-title-lg text-text-primary-inverse text-center leading-[1.1] px-space-fluid-4-8\">\n {message}\n </span>\n </div>\n )}\n </div>\n );\n}\n\nfunction CardAction({\n label,\n onClick,\n href,\n linkAs,\n}: {\n label: string;\n onClick?: () => void;\n href?: string;\n linkAs?: React.ElementType;\n}) {\n const classes =\n 'inline-flex items-center justify-center h-12 min-h-12 rounded-rad-md px-space-5 text-btn-reg bg-fill-action-primary text-text-action-primary hover:bg-fill-action-primary-hover active:bg-fill-action-primary-active transition-colors';\n\n if (href) {\n const LinkComponent = linkAs || 'a';\n return (\n <LinkComponent href={href} className={classes}>\n {label}\n </LinkComponent>\n );\n }\n\n return (\n <button type=\"button\" onClick={onClick} className={classes}>\n {label}\n </button>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Card\n// ---------------------------------------------------------------------------\n\n/**\n * Card with optional image, title, description, and action. Renders a bordered container\n * with design-token styling. Use for product cards, feature highlights, or content blocks.\n * For image-only or custom layouts use Box or a custom component.\n *\n * @example\n * <Card title=\"Feature\" description=\"Description here.\" type=\"text-only\" />\n *\n * @example\n * <Card type=\"media-horizontal\" imageSrc=\"/img.jpg\" imageAlt=\"Product\" title=\"Product\" label=\"View\" labelHref=\"/product\" />\n */\nexport function Card({\n type = 'text-only',\n imageSrc,\n imageAlt = '',\n imageWidth = 600,\n imageHeight = 400,\n imageQuality,\n imageUnoptimized,\n message,\n title,\n description,\n label,\n onLabelClick,\n labelHref,\n linkAs,\n className = '',\n ref,\n ...rest\n}: CardProps) {\n const isMedia = type !== 'text-only';\n const ratio = type === 'media-vertical' ? 'vertical' : 'horizontal';\n\n const rootClasses = [\n 'flex flex-col rounded-rad-md overflow-hidden',\n isMedia ? 'bg-fill-bg-secondary pb-space-fluid-4-8 gap-space-fluid-4-6' : 'bg-fill-bg-primary border border-border-light',\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n const contentClasses = [\n 'flex flex-col text-text-primary',\n isMedia ? 'gap-space-fluid-4-6 px-space-fluid-4-8' : 'gap-space-3 p-space-4',\n ]\n .filter(Boolean)\n .join(' ');\n\n return (\n <div ref={ref} className={rootClasses} {...rest}>\n {isMedia && imageSrc && (\n <CardImage\n src={imageSrc}\n alt={imageAlt}\n ratio={ratio}\n message={message}\n width={imageWidth}\n height={imageHeight}\n quality={imageQuality}\n unoptimized={imageUnoptimized}\n />\n )}\n\n <div className={contentClasses}>\n <div className=\"flex flex-col gap-space-1\">\n <h3 className=\"text-title-md leading-[1.2] tracking-[0.19px]\">{title}</h3>\n {description && <p className=\"text-text-md leading-[1.4]\">{description}</p>}\n </div>\n\n {label && (\n <div>\n <CardAction label={label} onClick={onLabelClick} href={labelHref} linkAs={linkAs} />\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"names":[],"mappings":";;AAqDA,SAAS,UAAU;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GASG;AACD,QAAM,cAAc,UAAU,eAAe,iBAAiB;AAE9D,SACE,qBAAC,OAAA,EAAI,WAAW,oDAAoD,WAAW,IAC7E,UAAA;AAAA,IAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,WAAU;AAAA,MAAA;AAAA,IAAA;AAAA,IAEX,WACC,oBAAC,OAAA,EAAI,WAAU,uEACb,8BAAC,QAAA,EAAK,WAAU,wFACb,UAAA,QAAA,CACH,EAAA,CACF;AAAA,EAAA,GAEJ;AAEJ;AAEA,SAAS,WAAW;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,QAAM,UACJ;AAEF,MAAI,MAAM;AACR,UAAM,gBAAgB,UAAU;AAChC,WACE,oBAAC,eAAA,EAAc,MAAY,WAAW,SACnC,UAAA,OACH;AAAA,EAEJ;AAEA,6BACG,UAAA,EAAO,MAAK,UAAS,SAAkB,WAAW,SAChD,UAAA,OACH;AAEJ;AAiBO,SAAS,KAAK;AAAA,EACnB,OAAO;AAAA,EACP;AAAA,EACA,WAAW;AAAA,EACX,aAAa;AAAA,EACb,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA,GAAG;AACL,GAAc;AACZ,QAAM,UAAU,SAAS;AACzB,QAAM,QAAQ,SAAS,mBAAmB,aAAa;AAEvD,QAAM,cAAc;AAAA,IAClB;AAAA,IACA,UAAU,gEAAgE;AAAA,IAC1E;AAAA,EAAA,EAEC,OAAO,OAAO,EACd,KAAK,GAAG;AAEX,QAAM,iBAAiB;AAAA,IACrB;AAAA,IACA,UAAU,2CAA2C;AAAA,EAAA,EAEpD,OAAO,OAAO,EACd,KAAK,GAAG;AAEX,8BACG,OAAA,EAAI,KAAU,WAAW,aAAc,GAAG,MACxC,UAAA;AAAA,IAAA,WAAW,YACV;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,KAAK;AAAA,QACL,KAAK;AAAA,QACL;AAAA,QACA;AAAA,QACA,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,aAAa;AAAA,MAAA;AAAA,IAAA;AAAA,IAIjB,qBAAC,OAAA,EAAI,WAAW,gBACd,UAAA;AAAA,MAAA,qBAAC,OAAA,EAAI,WAAU,6BACb,UAAA;AAAA,QAAA,oBAAC,MAAA,EAAG,WAAU,iDAAiD,UAAA,OAAM;AAAA,QACpE,eAAe,oBAAC,KAAA,EAAE,WAAU,8BAA8B,UAAA,YAAA,CAAY;AAAA,MAAA,GACzE;AAAA,MAEC,SACC,oBAAC,OAAA,EACC,UAAA,oBAAC,YAAA,EAAW,OAAc,SAAS,cAAc,MAAM,WAAW,OAAA,CAAgB,EAAA,CACpF;AAAA,IAAA,EAAA,CAEJ;AAAA,EAAA,GACF;AAEJ;"}
@@ -1 +1 @@
1
- {"version":3,"file":"FAQ.d.ts","sourceRoot":"","sources":["../../../src/components/FAQ/FAQ.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA0D,MAAM,OAAO,CAAC;AAC/E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAqB3C;;;GAGG;AACH,MAAM,WAAW,QAAS,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE,UAAU,CAAC;IACnF,yEAAyE;IACzE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6GAA6G;IAC7G,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACrC,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC1B,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACvC,uFAAuF;IACvF,IAAI,CAAC,EAAE;QACL,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,4EAA4E;IAC5E,MAAM,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC;IAC3B,wDAAwD;IACxD,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,GAAG,CAAC,EAClB,OAAe,EACf,KAAK,EACL,gBAAgB,EAChB,KAAK,EACL,QAAQ,EACR,IAAI,EACJ,MAAM,EACN,SAAc,EACd,GAAG,EACH,GAAG,IAAI,EACR,EAAE,QAAQ,2CAiJV"}
1
+ {"version":3,"file":"FAQ.d.ts","sourceRoot":"","sources":["../../../src/components/FAQ/FAQ.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA0D,MAAM,OAAO,CAAC;AAC/E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAqB3C;;;GAGG;AACH,MAAM,WAAW,QAAS,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE,UAAU,CAAC;IACnF,yEAAyE;IACzE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6GAA6G;IAC7G,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACrC,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC1B,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACvC,uFAAuF;IACvF,IAAI,CAAC,EAAE;QACL,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,4EAA4E;IAC5E,MAAM,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC;IAC3B,wDAAwD;IACxD,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,GAAG,CAAC,EAClB,OAAe,EACf,KAAK,EACL,gBAAgB,EAChB,KAAK,EACL,QAAQ,EACR,IAAI,EACJ,MAAM,EACN,SAAc,EACd,GAAG,EACH,GAAG,IAAI,EACR,EAAE,QAAQ,2CAmJV"}
@@ -103,7 +103,7 @@ function FAQ({
103
103
  {
104
104
  className: "flex flex-col gap-space-fluid-2-4 rounded-rad-md overflow-hidden bg-fill-action-inverse p-space-fluid-4-8",
105
105
  children: [
106
- /* @__PURE__ */ jsx("h3", { children: /* @__PURE__ */ jsxs(
106
+ /* @__PURE__ */ jsx("h3", { className: "contents", children: /* @__PURE__ */ jsxs(
107
107
  "button",
108
108
  {
109
109
  ref: (el) => {
@@ -111,7 +111,7 @@ function FAQ({
111
111
  },
112
112
  id: triggerId,
113
113
  type: "button",
114
- className: `flex w-full items-center justify-between gap-space-fluid-4-6 text-title-md text-left transition-colors outline-none ${disabled ? "text-text-disabled cursor-not-allowed" : "text-text-primary hover:bg-fill-bg-secondary focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-border-focus cursor-pointer"}`,
114
+ className: `flex w-full items-start justify-between gap-space-fluid-4-6 text-title-md text-text-primary text-left leading-[1.2] tracking-[0.19px] transition-colors outline-none min-h-px ${disabled ? "text-text-disabled cursor-not-allowed" : "hover:bg-fill-bg-secondary focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-border-focus cursor-pointer"}`,
115
115
  "aria-expanded": isOpen,
116
116
  "aria-controls": panelId,
117
117
  "aria-disabled": disabled || void 0,
@@ -119,13 +119,13 @@ function FAQ({
119
119
  onClick: () => toggle(index),
120
120
  onKeyDown: (e) => handleKeyDown(e, index),
121
121
  children: [
122
- /* @__PURE__ */ jsx("span", { children: item.question }),
123
- /* @__PURE__ */ jsx(
122
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1", children: item.question }),
123
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 size-6 flex items-center justify-center overflow-clip", "aria-hidden": true, children: /* @__PURE__ */ jsx(
124
124
  ChevronIcon,
125
125
  {
126
- className: `h-6 w-6 shrink-0 transition-transform duration-200 ${isOpen ? "rotate-180" : ""}`
126
+ className: `size-6 transition-transform duration-200 ${isOpen ? "rotate-180" : ""}`
127
127
  }
128
- )
128
+ ) })
129
129
  ]
130
130
  }
131
131
  ) }),
@@ -136,7 +136,7 @@ function FAQ({
136
136
  role: "region",
137
137
  "aria-labelledby": triggerId,
138
138
  className: `overflow-hidden transition-all duration-200 ease-in-out ${isOpen ? "max-h-[2000px] opacity-100" : "max-h-0 opacity-0"}`,
139
- children: /* @__PURE__ */ jsx("div", { className: "text-text-md text-text-primary", children: item.answer })
139
+ children: /* @__PURE__ */ jsx("div", { className: "text-text-md text-text-primary leading-[1.4] text-left w-full", children: item.answer })
140
140
  }
141
141
  )
142
142
  ]
@@ -1 +1 @@
1
- {"version":3,"file":"FAQ.js","sources":["../../../src/components/FAQ/FAQ.tsx"],"sourcesContent":["'use client';\n\nimport React, { useState, useRef, useCallback, useId, useEffect } from 'react';\nimport type { FAQItem } from '../../types';\n\nfunction ChevronIcon({ className }: { className?: string }) {\n return (\n <svg\n className={className}\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M6 9l6 6 6-6\" />\n </svg>\n );\n}\n\n/**\n * Props for the FAQ component. Each item uses the FAQItem shape: question, answer, disabled?.\n * @see FAQItem\n */\nexport interface FAQProps extends Omit<React.HTMLAttributes<HTMLElement>, 'onChange'> {\n /** Section heading displayed above the accordion list. @default 'FAQ' */\n heading?: string;\n /** Array of question/answer pairs. Each item: question (string), answer (ReactNode), disabled? (boolean). */\n items: FAQItem[];\n /** Index(es) of the item(s) open by default (uncontrolled). @default [] */\n defaultOpenIndex?: number | number[];\n /** Currently open index(es) (controlled). @default undefined */\n value?: number | number[];\n /** Called when the open indices change. @default undefined */\n onChange?: (indices: number[]) => void;\n /** Optional link rendered below the list (e.g. \"View all FAQs\"). @default undefined */\n link?: {\n label: string;\n href: string;\n };\n /** Component to render the footer link as (e.g. next/link). @default 'a' */\n linkAs?: React.ElementType;\n /** Forwarded ref for the section. @default undefined */\n ref?: React.Ref<HTMLElement>;\n}\n\n/**\n * FAQ section: heading plus an accordion of question/answer pairs. Uses FAQItem shape\n * (question, answer, optional disabled). Supports controlled (value/onChange) or uncontrolled\n * (defaultOpenIndex) expansion. Optional footer link for \"View all FAQs\" etc.\n *\n * Layout and spacing match Figma templates across breakpoints:\n * - xs+ (320px): section padding 16px/32px, heading ~25px, item padding 16px, list gap 8px.\n * - lg+ (940px) / xl+ (1392px): section padding 32px/64px, heading 32px, item padding 32px, list gap 8px, content gap 24px.\n * Uses design tokens: bg-fill-bg-secondary, bg-fill-action-inverse, text-title-lg (heading), text-title-md (question), text-text-md (answer), text-link-md (footer link), rad-md, fluid spacing.\n *\n * @example\n * <FAQ heading=\"Questions\" items={[{ question: 'What is X?', answer: 'X is...' }]} />\n *\n * @example\n * <FAQ items={faqItems} defaultOpenIndex={0} link={{ label: 'View all', href: '/faq' }} />\n */\nexport function FAQ({\n heading = 'FAQ',\n items,\n defaultOpenIndex,\n value,\n onChange,\n link,\n linkAs,\n className = '',\n ref,\n ...rest\n}: FAQProps) {\n const baseId = useId();\n const isControlled = value !== undefined;\n\n const controlledIndices = value !== undefined ? (Array.isArray(value) ? value : [value]) : undefined;\n const defaultIndices = defaultOpenIndex !== undefined ? (Array.isArray(defaultOpenIndex) ? defaultOpenIndex : [defaultOpenIndex]) : [];\n\n const [internalOpen, setInternalOpen] = useState<number[]>(defaultIndices);\n const openIndices = isControlled ? (controlledIndices || []) : internalOpen;\n\n useEffect(() => {\n if (!isControlled && defaultOpenIndex !== undefined) {\n setInternalOpen(defaultIndices);\n }\n }, [defaultOpenIndex, isControlled]);\n\n const triggerRefs = useRef<(HTMLButtonElement | null)[]>([]);\n\n const toggle = useCallback(\n (index: number) => {\n const next = openIndices.includes(index)\n ? openIndices.filter((i) => i !== index)\n : [...openIndices, index];\n\n if (isControlled) {\n onChange?.(next);\n } else {\n setInternalOpen(next);\n }\n },\n [openIndices, isControlled, onChange],\n );\n\n const focusTrigger = (index: number) => {\n triggerRefs.current[index]?.focus();\n };\n\n const handleKeyDown = (e: React.KeyboardEvent, index: number) => {\n const enabledIndices = items\n .map((_, i) => i)\n .filter((i) => !items[i].disabled);\n const currentPos = enabledIndices.indexOf(index);\n\n switch (e.key) {\n case 'ArrowDown': {\n e.preventDefault();\n focusTrigger(enabledIndices[(currentPos + 1) % enabledIndices.length]);\n break;\n }\n case 'ArrowUp': {\n e.preventDefault();\n focusTrigger(enabledIndices[(currentPos - 1 + enabledIndices.length) % enabledIndices.length]);\n break;\n }\n case 'Home': {\n e.preventDefault();\n focusTrigger(enabledIndices[0]);\n break;\n }\n case 'End': {\n e.preventDefault();\n focusTrigger(enabledIndices[enabledIndices.length - 1]);\n break;\n }\n }\n };\n\n return (\n <section\n ref={ref}\n className={`w-full bg-fill-bg-secondary px-space-fluid-4-8 py-space-fluid-8-16 ${className}`}\n {...rest}\n >\n <div className=\"mx-auto w-full max-w-[1328px]\">\n <div className=\"flex flex-col gap-space-fluid-4-5\">\n {heading && (\n <h2 className=\"text-title-lg text-text-primary\">{heading}</h2>\n )}\n\n <div className=\"flex flex-col gap-space-2\">\n {items.map((item, index) => {\n const isOpen = openIndices.includes(index);\n const triggerId = `${baseId}-faq-trigger-${index}`;\n const panelId = `${baseId}-faq-panel-${index}`;\n const disabled = !!item.disabled;\n\n return (\n <div\n key={index}\n className=\"flex flex-col gap-space-fluid-2-4 rounded-rad-md overflow-hidden bg-fill-action-inverse p-space-fluid-4-8\"\n >\n <h3>\n <button\n ref={(el) => { triggerRefs.current[index] = el; }}\n id={triggerId}\n type=\"button\"\n className={`flex w-full items-center justify-between gap-space-fluid-4-6 text-title-md text-left transition-colors outline-none ${disabled\n ? 'text-text-disabled cursor-not-allowed'\n : 'text-text-primary hover:bg-fill-bg-secondary focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-border-focus cursor-pointer'\n }`}\n aria-expanded={isOpen}\n aria-controls={panelId}\n aria-disabled={disabled || undefined}\n disabled={disabled}\n onClick={() => toggle(index)}\n onKeyDown={(e) => handleKeyDown(e, index)}\n >\n <span>{item.question}</span>\n <ChevronIcon\n className={`h-6 w-6 shrink-0 transition-transform duration-200 ${isOpen ? 'rotate-180' : ''}`}\n />\n </button>\n </h3>\n <div\n id={panelId}\n role=\"region\"\n aria-labelledby={triggerId}\n className={`overflow-hidden transition-all duration-200 ease-in-out ${isOpen ? 'max-h-[2000px] opacity-100' : 'max-h-0 opacity-0'\n }`}\n >\n <div className=\"text-text-md text-text-primary\">\n {item.answer}\n </div>\n </div>\n </div>\n );\n })}\n </div>\n </div>\n\n {link && (\n <div className=\"mt-space-fluid-4-6\">\n {React.createElement(\n linkAs || 'a',\n {\n href: link.href,\n className: 'text-link-md text-link-primary underline hover:text-link-primary-hover transition-colors',\n },\n link.label,\n )}\n </div>\n )}\n </div>\n </section>\n );\n}\n"],"names":[],"mappings":";;;AAKA;AACE;AACE;AAAC;AAAA;AACC;AACM;AACC;AACC;AACH;AACE;AACK;AACE;AACC;AACH;AAEW;AAAA;AAG7B;AA4CO;AAAa;AACR;AACV;AACA;AACA;AACA;AACA;AACA;AACY;AACZ;AAEF;AACE;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACE;AACE;AAA8B;AAChC;AAGF;AAEA;AAAe;AAEX;AAIA;AACE;AAAW;AAEX;AAAoB;AACtB;AACF;AACoC;AAGtC;;AACE;AAA4B;AAG9B;AACE;AAGA;AAEA;AAAU;AAEN;AACA;AACA;AAAA;AACF;AAEE;AACA;AACA;AAAA;AACF;AAEE;AACA;AACA;AAAA;AACF;AAEE;AACA;AACA;AAAA;AACF;AAAA;AAIJ;AACE;AAAC;AAAA;AACC;AAC0F;AACtF;AAGF;AACG;AAC0D;AAKvD;AACA;AACA;AACA;AAEA;AACE;AAAC;AAAA;AAEW;AAEV;AACE;AAAC;AAAA;AACgB;AAA6B;AAAI;AAC5C;AACC;AAIH;AACa;AACA;AACY;AAC3B;AAC2B;AACa;AAExC;AAAqB;AACrB;AAAC;AAAA;AAC4F;AAAA;AAAA;AAC7F;AAAA;AAEJ;AACA;AAAC;AAAA;AACK;AACC;AACY;AAEf;AAIF;AAAA;AAAA;AACF;AAAA;AAnCK;AAAA;AAuCb;AACF;AAIW;AACK;AACV;AACa;AACA;AAAA;AAER;AAET;AAEJ;AAAA;AAGN;;;;"}
1
+ {"version":3,"file":"FAQ.js","sources":["../../../src/components/FAQ/FAQ.tsx"],"sourcesContent":["'use client';\n\nimport React, { useState, useRef, useCallback, useId, useEffect } from 'react';\nimport type { FAQItem } from '../../types';\n\nfunction ChevronIcon({ className }: { className?: string }) {\n return (\n <svg\n className={className}\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M6 9l6 6 6-6\" />\n </svg>\n );\n}\n\n/**\n * Props for the FAQ component. Each item uses the FAQItem shape: question, answer, disabled?.\n * @see FAQItem\n */\nexport interface FAQProps extends Omit<React.HTMLAttributes<HTMLElement>, 'onChange'> {\n /** Section heading displayed above the accordion list. @default 'FAQ' */\n heading?: string;\n /** Array of question/answer pairs. Each item: question (string), answer (ReactNode), disabled? (boolean). */\n items: FAQItem[];\n /** Index(es) of the item(s) open by default (uncontrolled). @default [] */\n defaultOpenIndex?: number | number[];\n /** Currently open index(es) (controlled). @default undefined */\n value?: number | number[];\n /** Called when the open indices change. @default undefined */\n onChange?: (indices: number[]) => void;\n /** Optional link rendered below the list (e.g. \"View all FAQs\"). @default undefined */\n link?: {\n label: string;\n href: string;\n };\n /** Component to render the footer link as (e.g. next/link). @default 'a' */\n linkAs?: React.ElementType;\n /** Forwarded ref for the section. @default undefined */\n ref?: React.Ref<HTMLElement>;\n}\n\n/**\n * FAQ section: heading plus an accordion of question/answer pairs. Uses FAQItem shape\n * (question, answer, optional disabled). Supports controlled (value/onChange) or uncontrolled\n * (defaultOpenIndex) expansion. Optional footer link for \"View all FAQs\" etc.\n *\n * Layout and spacing match Figma templates across breakpoints:\n * - xs+ (320px): section padding 16px/32px, heading ~25px, item padding 16px, list gap 8px.\n * - lg+ (940px) / xl+ (1392px): section padding 32px/64px, heading 32px, item padding 32px, list gap 8px, content gap 24px.\n * Uses design tokens: bg-fill-bg-secondary, bg-fill-action-inverse, text-title-lg (heading), text-title-md (question), text-text-md (answer), text-link-md (footer link), rad-md, fluid spacing.\n *\n * @example\n * <FAQ heading=\"Questions\" items={[{ question: 'What is X?', answer: 'X is...' }]} />\n *\n * @example\n * <FAQ items={faqItems} defaultOpenIndex={0} link={{ label: 'View all', href: '/faq' }} />\n */\nexport function FAQ({\n heading = 'FAQ',\n items,\n defaultOpenIndex,\n value,\n onChange,\n link,\n linkAs,\n className = '',\n ref,\n ...rest\n}: FAQProps) {\n const baseId = useId();\n const isControlled = value !== undefined;\n\n const controlledIndices = value !== undefined ? (Array.isArray(value) ? value : [value]) : undefined;\n const defaultIndices = defaultOpenIndex !== undefined ? (Array.isArray(defaultOpenIndex) ? defaultOpenIndex : [defaultOpenIndex]) : [];\n\n const [internalOpen, setInternalOpen] = useState<number[]>(defaultIndices);\n const openIndices = isControlled ? (controlledIndices || []) : internalOpen;\n\n useEffect(() => {\n if (!isControlled && defaultOpenIndex !== undefined) {\n setInternalOpen(defaultIndices);\n }\n }, [defaultOpenIndex, isControlled]);\n\n const triggerRefs = useRef<(HTMLButtonElement | null)[]>([]);\n\n const toggle = useCallback(\n (index: number) => {\n const next = openIndices.includes(index)\n ? openIndices.filter((i) => i !== index)\n : [...openIndices, index];\n\n if (isControlled) {\n onChange?.(next);\n } else {\n setInternalOpen(next);\n }\n },\n [openIndices, isControlled, onChange],\n );\n\n const focusTrigger = (index: number) => {\n triggerRefs.current[index]?.focus();\n };\n\n const handleKeyDown = (e: React.KeyboardEvent, index: number) => {\n const enabledIndices = items\n .map((_, i) => i)\n .filter((i) => !items[i].disabled);\n const currentPos = enabledIndices.indexOf(index);\n\n switch (e.key) {\n case 'ArrowDown': {\n e.preventDefault();\n focusTrigger(enabledIndices[(currentPos + 1) % enabledIndices.length]);\n break;\n }\n case 'ArrowUp': {\n e.preventDefault();\n focusTrigger(enabledIndices[(currentPos - 1 + enabledIndices.length) % enabledIndices.length]);\n break;\n }\n case 'Home': {\n e.preventDefault();\n focusTrigger(enabledIndices[0]);\n break;\n }\n case 'End': {\n e.preventDefault();\n focusTrigger(enabledIndices[enabledIndices.length - 1]);\n break;\n }\n }\n };\n\n return (\n <section\n ref={ref}\n className={`w-full bg-fill-bg-secondary px-space-fluid-4-8 py-space-fluid-8-16 ${className}`}\n {...rest}\n >\n <div className=\"mx-auto w-full max-w-[1328px]\">\n <div className=\"flex flex-col gap-space-fluid-4-5\">\n {heading && (\n <h2 className=\"text-title-lg text-text-primary\">{heading}</h2>\n )}\n\n <div className=\"flex flex-col gap-space-2\">\n {items.map((item, index) => {\n const isOpen = openIndices.includes(index);\n const triggerId = `${baseId}-faq-trigger-${index}`;\n const panelId = `${baseId}-faq-panel-${index}`;\n const disabled = !!item.disabled;\n\n return (\n <div\n key={index}\n className=\"flex flex-col gap-space-fluid-2-4 rounded-rad-md overflow-hidden bg-fill-action-inverse p-space-fluid-4-8\"\n >\n <h3 className=\"contents\">\n <button\n ref={(el) => { triggerRefs.current[index] = el; }}\n id={triggerId}\n type=\"button\"\n className={`flex w-full items-start justify-between gap-space-fluid-4-6 text-title-md text-text-primary text-left leading-[1.2] tracking-[0.19px] transition-colors outline-none min-h-px ${disabled\n ? 'text-text-disabled cursor-not-allowed'\n : 'hover:bg-fill-bg-secondary focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-border-focus cursor-pointer'\n }`}\n aria-expanded={isOpen}\n aria-controls={panelId}\n aria-disabled={disabled || undefined}\n disabled={disabled}\n onClick={() => toggle(index)}\n onKeyDown={(e) => handleKeyDown(e, index)}\n >\n <span className=\"min-w-0 flex-1\">{item.question}</span>\n <span className=\"shrink-0 size-6 flex items-center justify-center overflow-clip\" aria-hidden>\n <ChevronIcon\n className={`size-6 transition-transform duration-200 ${isOpen ? 'rotate-180' : ''}`}\n />\n </span>\n </button>\n </h3>\n <div\n id={panelId}\n role=\"region\"\n aria-labelledby={triggerId}\n className={`overflow-hidden transition-all duration-200 ease-in-out ${isOpen ? 'max-h-[2000px] opacity-100' : 'max-h-0 opacity-0'\n }`}\n >\n <div className=\"text-text-md text-text-primary leading-[1.4] text-left w-full\">\n {item.answer}\n </div>\n </div>\n </div>\n );\n })}\n </div>\n </div>\n\n {link && (\n <div className=\"mt-space-fluid-4-6\">\n {React.createElement(\n linkAs || 'a',\n {\n href: link.href,\n className: 'text-link-md text-link-primary underline hover:text-link-primary-hover transition-colors',\n },\n link.label,\n )}\n </div>\n )}\n </div>\n </section>\n );\n}\n"],"names":[],"mappings":";;;AAKA;AACE;AACE;AAAC;AAAA;AACC;AACM;AACC;AACC;AACH;AACE;AACK;AACE;AACC;AACH;AAEW;AAAA;AAG7B;AA4CO;AAAa;AACR;AACV;AACA;AACA;AACA;AACA;AACA;AACY;AACZ;AAEF;AACE;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACE;AACE;AAA8B;AAChC;AAGF;AAEA;AAAe;AAEX;AAIA;AACE;AAAW;AAEX;AAAoB;AACtB;AACF;AACoC;AAGtC;;AACE;AAA4B;AAG9B;AACE;AAGA;AAEA;AAAU;AAEN;AACA;AACA;AAAA;AACF;AAEE;AACA;AACA;AAAA;AACF;AAEE;AACA;AACA;AAAA;AACF;AAEE;AACA;AACA;AAAA;AACF;AAAA;AAIJ;AACE;AAAC;AAAA;AACC;AAC0F;AACtF;AAGF;AACG;AAC0D;AAKvD;AACA;AACA;AACA;AAEA;AACE;AAAC;AAAA;AAEW;AAEV;AACE;AAAC;AAAA;AACgB;AAA6B;AAAI;AAC5C;AACC;AAIH;AACa;AACA;AACY;AAC3B;AAC2B;AACa;AAExC;AAAgD;AAE9C;AAAC;AAAA;AACkF;AAAA;AAErF;AAAA;AAAA;AAEJ;AACA;AAAC;AAAA;AACK;AACC;AACY;AAEf;AAIF;AAAA;AAAA;AACF;AAAA;AArCK;AAAA;AAyCb;AACF;AAIW;AACK;AACV;AACa;AACA;AAAA;AAER;AAET;AAEJ;AAAA;AAGN;;;;"}
@@ -3,11 +3,12 @@ import { ListProps, ListItemProps } from '../../types/list';
3
3
  * Semantic list component. Renders a <ul> or <ol> with consistent
4
4
  * marker styling and item spacing using design system tokens.
5
5
  *
6
- * List owns two concerns: semantic HTML structure and marker appearance.
7
- * It does not own typography apply size via List.Item's size prop.
8
- * It does not own layout wrap List in Stack or Grid for layout needs.
6
+ * For marker="disc" and marker="decimal", the list uses block layout and
7
+ * margin on items (not flex/gap) so list-style-position: outside and markers
8
+ * render reliably. For marker="none", flex + gap is used.
9
9
  *
10
- * Use List.Item as the direct child of List.
10
+ * Use indent (e.g. indent="space-list-indent" for 27px) and itemPadding="none"
11
+ * for legal/body-copy lists.
11
12
  *
12
13
  * @example
13
14
  * // Unordered feature list
@@ -17,10 +18,10 @@ import { ListProps, ListItemProps } from '../../types/list';
17
18
  * </List>
18
19
  *
19
20
  * @example
20
- * // Ordered steps
21
- * <List marker="decimal" spacing="space-4">
22
- * <List.Item size="md">Apply online</List.Item>
23
- * <List.Item size="md">Get approved</List.Item>
21
+ * // Legal/body-copy list (27px indent, no extra item padding)
22
+ * <List indent="space-list-indent" itemPadding="none" spacing="space-2">
23
+ * <List.Item size="md">First term.</List.Item>
24
+ * <List.Item size="md">Second term.</List.Item>
24
25
  * </List>
25
26
  *
26
27
  * @example
@@ -29,7 +30,7 @@ import { ListProps, ListItemProps } from '../../types/list';
29
30
  * <List.Item><Link href="/about">About</Link></List.Item>
30
31
  * </List>
31
32
  */
32
- export declare function List({ marker, spacing, className, style, children, }: ListProps): import("react/jsx-runtime").JSX.Element;
33
+ export declare function List({ marker, spacing, indent, itemPadding, className, style, children, }: ListProps): import("react/jsx-runtime").JSX.Element;
33
34
  export declare namespace List {
34
35
  var Item: ({ size, className, style, children, }: ListItemProps) => import("react/jsx-runtime").JSX.Element;
35
36
  }
@@ -1 +1 @@
1
- {"version":3,"file":"List.d.ts","sourceRoot":"","sources":["../../../src/components/List/List.tsx"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,SAAS,EACd,KAAK,aAAa,EACnB,MAAM,kBAAkB,CAAC;AAS1B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,IAAI,CAAC,EACnB,MAAe,EACf,OAAmB,EACnB,SAAc,EACd,KAAK,EACL,QAAQ,GACT,EAAE,SAAS,2CAqBX;yBA3Be,IAAI;sDAsCjB,aAAa"}
1
+ {"version":3,"file":"List.d.ts","sourceRoot":"","sources":["../../../src/components/List/List.tsx"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,SAAS,EACd,KAAK,aAAa,EACnB,MAAM,kBAAkB,CAAC;AAuB1B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,IAAI,CAAC,EACnB,MAAe,EACf,OAAmB,EACnB,MAAM,EACN,WAAuB,EACvB,SAAc,EACd,KAAK,EACL,QAAQ,GACT,EAAE,SAAS,2CA+BX;yBAvCe,IAAI;sDAkDjB,aAAa"}
@@ -6,25 +6,40 @@ const markerClassMap = {
6
6
  decimal: "jiwambe-list--decimal",
7
7
  none: "jiwambe-list--none"
8
8
  };
9
+ function listSpacingClassSuffix(token) {
10
+ return token.startsWith("fluid-") ? `space-${token}` : token;
11
+ }
12
+ function resolveIndent(indent) {
13
+ if (indent == null) return void 0;
14
+ if (indent.startsWith("fluid-")) return `var(--space-${indent})`;
15
+ if (indent.startsWith("space-")) return `var(--${indent})`;
16
+ return indent;
17
+ }
9
18
  function List({
10
19
  marker = "disc",
11
20
  spacing = "space-2",
21
+ indent,
22
+ itemPadding = "default",
12
23
  className = "",
13
24
  style,
14
25
  children
15
26
  }) {
16
27
  const Tag = marker === "decimal" ? "ol" : "ul";
17
28
  const markerClass = markerClassMap[marker];
18
- const gapClass = gapMap[spacing];
29
+ const useBlockLayout = marker === "disc" || marker === "decimal";
30
+ const spacingClass = useBlockLayout ? `jiwambe-list--spacing-${listSpacingClassSuffix(spacing)}` : gapMap[spacing];
31
+ const itemPaddingClass = itemPadding === "none" ? "jiwambe-list--item-padding-none" : "";
19
32
  const classes = [
20
33
  "jiwambe-list",
21
34
  markerClass,
22
- "flex",
23
- "flex-col",
24
- gapClass,
35
+ useBlockLayout ? "" : "flex flex-col",
36
+ spacingClass,
37
+ itemPaddingClass,
25
38
  className
26
39
  ].filter(Boolean).join(" ");
27
- return /* @__PURE__ */ jsx(Tag, { className: classes, style, children });
40
+ const resolvedIndent = resolveIndent(indent);
41
+ const combinedStyle = resolvedIndent != null ? { ...style, paddingLeft: resolvedIndent } : style;
42
+ return /* @__PURE__ */ jsx(Tag, { className: classes, style: combinedStyle, children });
28
43
  }
29
44
  List.Item = function ListItem({
30
45
  size = "sm",
@@ -1 +1 @@
1
- {"version":3,"file":"List.js","sources":["../../../src/components/List/List.tsx"],"sourcesContent":["import {\n listItemSizeClassMap,\n type ListProps,\n type ListItemProps,\n} from '../../types/list';\nimport { gapMap } from '../../utils/spacing';\n\nconst markerClassMap: Record<string, string> = {\n disc: 'jiwambe-list--disc',\n decimal: 'jiwambe-list--decimal',\n none: 'jiwambe-list--none',\n};\n\n/**\n * Semantic list component. Renders a <ul> or <ol> with consistent\n * marker styling and item spacing using design system tokens.\n *\n * List owns two concerns: semantic HTML structure and marker appearance.\n * It does not own typography apply size via List.Item's size prop.\n * It does not own layout wrap List in Stack or Grid for layout needs.\n *\n * Use List.Item as the direct child of List.\n *\n * @example\n * // Unordered feature list\n * <List spacing=\"space-3\">\n * <List.Item size=\"sm\">Fleet Taxi financing</List.Item>\n * <List.Item size=\"sm\">Home improvement loans</List.Item>\n * </List>\n *\n * @example\n * // Ordered steps\n * <List marker=\"decimal\" spacing=\"space-4\">\n * <List.Item size=\"md\">Apply online</List.Item>\n * <List.Item size=\"md\">Get approved</List.Item>\n * </List>\n *\n * @example\n * // Navigation links — no markers\n * <List marker=\"none\" spacing=\"space-1\">\n * <List.Item><Link href=\"/about\">About</Link></List.Item>\n * </List>\n */\nexport function List({\n marker = 'disc',\n spacing = 'space-2',\n className = '',\n style,\n children,\n}: ListProps) {\n const Tag = marker === 'decimal' ? 'ol' : 'ul';\n const markerClass = markerClassMap[marker];\n const gapClass = gapMap[spacing];\n\n const classes = [\n 'jiwambe-list',\n markerClass,\n 'flex',\n 'flex-col',\n gapClass,\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n return (\n <Tag className={classes} style={style}>\n {children}\n </Tag>\n );\n}\n\n/**\n * Renders a single list item (<li>) with design system typography and\n * marker styling. Use only as a direct child of List.\n */\nList.Item = function ListItem({\n size = 'sm',\n className = '',\n style,\n children,\n}: ListItemProps) {\n const typographyClass = listItemSizeClassMap[size];\n\n const classes = ['jiwambe-list-item', typographyClass, className]\n .filter(Boolean)\n .join(' ');\n\n return (\n <li className={classes} style={style}>\n {children}\n </li>\n );\n};\n"],"names":[],"mappings":";;;AAOA,MAAM,iBAAyC;AAAA,EAC7C,MAAM;AAAA,EACN,SAAS;AAAA,EACT,MAAM;AACR;AAgCO,SAAS,KAAK;AAAA,EACnB,SAAS;AAAA,EACT,UAAU;AAAA,EACV,YAAY;AAAA,EACZ;AAAA,EACA;AACF,GAAc;AACZ,QAAM,MAAM,WAAW,YAAY,OAAO;AAC1C,QAAM,cAAc,eAAe,MAAM;AACzC,QAAM,WAAW,OAAO,OAAO;AAE/B,QAAM,UAAU;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,EAEC,OAAO,OAAO,EACd,KAAK,GAAG;AAEX,SACE,oBAAC,KAAA,EAAI,WAAW,SAAS,OACtB,UACH;AAEJ;AAMA,KAAK,OAAO,SAAS,SAAS;AAAA,EAC5B,OAAO;AAAA,EACP,YAAY;AAAA,EACZ;AAAA,EACA;AACF,GAAkB;AAChB,QAAM,kBAAkB,qBAAqB,IAAI;AAEjD,QAAM,UAAU,CAAC,qBAAqB,iBAAiB,SAAS,EAC7D,OAAO,OAAO,EACd,KAAK,GAAG;AAEX,SACE,oBAAC,MAAA,EAAG,WAAW,SAAS,OACrB,UACH;AAEJ;"}
1
+ {"version":3,"file":"List.js","sources":["../../../src/components/List/List.tsx"],"sourcesContent":["import {\n listItemSizeClassMap,\n type ListProps,\n type ListItemProps,\n} from '../../types/list';\nimport { gapMap } from '../../utils/spacing';\nimport type { SpacingToken } from '../../types/spacing';\n\nconst markerClassMap: Record<string, string> = {\n disc: 'jiwambe-list--disc',\n decimal: 'jiwambe-list--decimal',\n none: 'jiwambe-list--none',\n};\n\n/** Maps spacing prop to plugin class suffix (jiwambe-list--spacing-{suffix}). */\nfunction listSpacingClassSuffix(token: SpacingToken): string {\n return token.startsWith('fluid-') ? `space-${token}` : token;\n}\n\n/** Resolves indent to CSS value: token → var(--space-X), custom string as-is. */\nfunction resolveIndent(indent: ListProps['indent']): string | undefined {\n if (indent == null) return undefined;\n if (indent.startsWith('fluid-')) return `var(--space-${indent})`;\n if (indent.startsWith('space-')) return `var(--${indent})`;\n return indent;\n}\n\n/**\n * Semantic list component. Renders a <ul> or <ol> with consistent\n * marker styling and item spacing using design system tokens.\n *\n * For marker=\"disc\" and marker=\"decimal\", the list uses block layout and\n * margin on items (not flex/gap) so list-style-position: outside and markers\n * render reliably. For marker=\"none\", flex + gap is used.\n *\n * Use indent (e.g. indent=\"space-list-indent\" for 27px) and itemPadding=\"none\"\n * for legal/body-copy lists.\n *\n * @example\n * // Unordered feature list\n * <List spacing=\"space-3\">\n * <List.Item size=\"sm\">Fleet Taxi financing</List.Item>\n * <List.Item size=\"sm\">Home improvement loans</List.Item>\n * </List>\n *\n * @example\n * // Legal/body-copy list (27px indent, no extra item padding)\n * <List indent=\"space-list-indent\" itemPadding=\"none\" spacing=\"space-2\">\n * <List.Item size=\"md\">First term.</List.Item>\n * <List.Item size=\"md\">Second term.</List.Item>\n * </List>\n *\n * @example\n * // Navigation links — no markers\n * <List marker=\"none\" spacing=\"space-1\">\n * <List.Item><Link href=\"/about\">About</Link></List.Item>\n * </List>\n */\nexport function List({\n marker = 'disc',\n spacing = 'space-2',\n indent,\n itemPadding = 'default',\n className = '',\n style,\n children,\n}: ListProps) {\n const Tag = marker === 'decimal' ? 'ol' : 'ul';\n const markerClass = markerClassMap[marker];\n const useBlockLayout = marker === 'disc' || marker === 'decimal';\n const spacingClass = useBlockLayout\n ? `jiwambe-list--spacing-${listSpacingClassSuffix(spacing)}`\n : gapMap[spacing];\n const itemPaddingClass = itemPadding === 'none' ? 'jiwambe-list--item-padding-none' : '';\n\n const classes = [\n 'jiwambe-list',\n markerClass,\n useBlockLayout ? '' : 'flex flex-col',\n spacingClass,\n itemPaddingClass,\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n const resolvedIndent = resolveIndent(indent);\n const combinedStyle =\n resolvedIndent != null\n ? { ...style, paddingLeft: resolvedIndent }\n : style;\n\n return (\n <Tag className={classes} style={combinedStyle}>\n {children}\n </Tag>\n );\n}\n\n/**\n * Renders a single list item (<li>) with design system typography and\n * marker styling. Use only as a direct child of List.\n */\nList.Item = function ListItem({\n size = 'sm',\n className = '',\n style,\n children,\n}: ListItemProps) {\n const typographyClass = listItemSizeClassMap[size];\n\n const classes = ['jiwambe-list-item', typographyClass, className]\n .filter(Boolean)\n .join(' ');\n\n return (\n <li className={classes} style={style}>\n {children}\n </li>\n );\n};\n"],"names":[],"mappings":";;;AAQA,MAAM,iBAAyC;AAAA,EAC7C,MAAM;AAAA,EACN,SAAS;AAAA,EACT,MAAM;AACR;AAGA,SAAS,uBAAuB,OAA6B;AAC3D,SAAO,MAAM,WAAW,QAAQ,IAAI,SAAS,KAAK,KAAK;AACzD;AAGA,SAAS,cAAc,QAAiD;AACtE,MAAI,UAAU,KAAM,QAAO;AAC3B,MAAI,OAAO,WAAW,QAAQ,EAAG,QAAO,eAAe,MAAM;AAC7D,MAAI,OAAO,WAAW,QAAQ,EAAG,QAAO,SAAS,MAAM;AACvD,SAAO;AACT;AAiCO,SAAS,KAAK;AAAA,EACnB,SAAS;AAAA,EACT,UAAU;AAAA,EACV;AAAA,EACA,cAAc;AAAA,EACd,YAAY;AAAA,EACZ;AAAA,EACA;AACF,GAAc;AACZ,QAAM,MAAM,WAAW,YAAY,OAAO;AAC1C,QAAM,cAAc,eAAe,MAAM;AACzC,QAAM,iBAAiB,WAAW,UAAU,WAAW;AACvD,QAAM,eAAe,iBACjB,yBAAyB,uBAAuB,OAAO,CAAC,KACxD,OAAO,OAAO;AAClB,QAAM,mBAAmB,gBAAgB,SAAS,oCAAoC;AAEtF,QAAM,UAAU;AAAA,IACd;AAAA,IACA;AAAA,IACA,iBAAiB,KAAK;AAAA,IACtB;AAAA,IACA;AAAA,IACA;AAAA,EAAA,EAEC,OAAO,OAAO,EACd,KAAK,GAAG;AAEX,QAAM,iBAAiB,cAAc,MAAM;AAC3C,QAAM,gBACJ,kBAAkB,OACd,EAAE,GAAG,OAAO,aAAa,mBACzB;AAEN,6BACG,KAAA,EAAI,WAAW,SAAS,OAAO,eAC7B,UACH;AAEJ;AAMA,KAAK,OAAO,SAAS,SAAS;AAAA,EAC5B,OAAO;AAAA,EACP,YAAY;AAAA,EACZ;AAAA,EACA;AACF,GAAkB;AAChB,QAAM,kBAAkB,qBAAqB,IAAI;AAEjD,QAAM,UAAU,CAAC,qBAAqB,iBAAiB,SAAS,EAC7D,OAAO,OAAO,EACd,KAAK,GAAG;AAEX,SACE,oBAAC,MAAA,EAAG,WAAW,SAAS,OACrB,UACH;AAEJ;"}