@lutra-ui-system/react 0.1.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.
Files changed (173) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +240 -0
  3. package/dist/components/button/button.d.ts +105 -0
  4. package/dist/components/button/button.d.ts.map +1 -0
  5. package/dist/components/button/button.js +91 -0
  6. package/dist/components/button/button.js.map +1 -0
  7. package/dist/components/button/index.d.ts +3 -0
  8. package/dist/components/button/index.d.ts.map +1 -0
  9. package/dist/components/button/index.js +2 -0
  10. package/dist/components/button/index.js.map +1 -0
  11. package/dist/components/card/card.d.ts +100 -0
  12. package/dist/components/card/card.d.ts.map +1 -0
  13. package/dist/components/card/card.js +51 -0
  14. package/dist/components/card/card.js.map +1 -0
  15. package/dist/components/card/index.d.ts +3 -0
  16. package/dist/components/card/index.d.ts.map +1 -0
  17. package/dist/components/card/index.js +2 -0
  18. package/dist/components/card/index.js.map +1 -0
  19. package/dist/components/checkbox/checkbox.d.ts +67 -0
  20. package/dist/components/checkbox/checkbox.d.ts.map +1 -0
  21. package/dist/components/checkbox/checkbox.js +63 -0
  22. package/dist/components/checkbox/checkbox.js.map +1 -0
  23. package/dist/components/checkbox/index.d.ts +3 -0
  24. package/dist/components/checkbox/index.d.ts.map +1 -0
  25. package/dist/components/checkbox/index.js +2 -0
  26. package/dist/components/checkbox/index.js.map +1 -0
  27. package/dist/components/dialog/dialog.d.ts +88 -0
  28. package/dist/components/dialog/dialog.d.ts.map +1 -0
  29. package/dist/components/dialog/dialog.js +156 -0
  30. package/dist/components/dialog/dialog.js.map +1 -0
  31. package/dist/components/dialog/index.d.ts +3 -0
  32. package/dist/components/dialog/index.d.ts.map +1 -0
  33. package/dist/components/dialog/index.js +2 -0
  34. package/dist/components/dialog/index.js.map +1 -0
  35. package/dist/components/dropdown/dropdown.d.ts +89 -0
  36. package/dist/components/dropdown/dropdown.d.ts.map +1 -0
  37. package/dist/components/dropdown/dropdown.js +173 -0
  38. package/dist/components/dropdown/dropdown.js.map +1 -0
  39. package/dist/components/dropdown/index.d.ts +3 -0
  40. package/dist/components/dropdown/index.d.ts.map +1 -0
  41. package/dist/components/dropdown/index.js +2 -0
  42. package/dist/components/dropdown/index.js.map +1 -0
  43. package/dist/components/error-summary/error-summary.d.ts +81 -0
  44. package/dist/components/error-summary/error-summary.d.ts.map +1 -0
  45. package/dist/components/error-summary/error-summary.js +85 -0
  46. package/dist/components/error-summary/error-summary.js.map +1 -0
  47. package/dist/components/error-summary/index.d.ts +3 -0
  48. package/dist/components/error-summary/index.d.ts.map +1 -0
  49. package/dist/components/error-summary/index.js +2 -0
  50. package/dist/components/error-summary/index.js.map +1 -0
  51. package/dist/components/field/field.d.ts +55 -0
  52. package/dist/components/field/field.d.ts.map +1 -0
  53. package/dist/components/field/field.js +26 -0
  54. package/dist/components/field/field.js.map +1 -0
  55. package/dist/components/field/index.d.ts +3 -0
  56. package/dist/components/field/index.d.ts.map +1 -0
  57. package/dist/components/field/index.js +2 -0
  58. package/dist/components/field/index.js.map +1 -0
  59. package/dist/components/field-group/field-group.d.ts +67 -0
  60. package/dist/components/field-group/field-group.d.ts.map +1 -0
  61. package/dist/components/field-group/field-group.js +38 -0
  62. package/dist/components/field-group/field-group.js.map +1 -0
  63. package/dist/components/field-group/index.d.ts +3 -0
  64. package/dist/components/field-group/index.d.ts.map +1 -0
  65. package/dist/components/field-group/index.js +2 -0
  66. package/dist/components/field-group/index.js.map +1 -0
  67. package/dist/components/input/index.d.ts +3 -0
  68. package/dist/components/input/index.d.ts.map +1 -0
  69. package/dist/components/input/index.js +2 -0
  70. package/dist/components/input/index.js.map +1 -0
  71. package/dist/components/input/input.d.ts +96 -0
  72. package/dist/components/input/input.d.ts.map +1 -0
  73. package/dist/components/input/input.js +26 -0
  74. package/dist/components/input/input.js.map +1 -0
  75. package/dist/components/link/index.d.ts +3 -0
  76. package/dist/components/link/index.d.ts.map +1 -0
  77. package/dist/components/link/index.js +2 -0
  78. package/dist/components/link/index.js.map +1 -0
  79. package/dist/components/link/link.d.ts +44 -0
  80. package/dist/components/link/link.d.ts.map +1 -0
  81. package/dist/components/link/link.js +26 -0
  82. package/dist/components/link/link.js.map +1 -0
  83. package/dist/components/link-card/index.d.ts +3 -0
  84. package/dist/components/link-card/index.d.ts.map +1 -0
  85. package/dist/components/link-card/index.js +2 -0
  86. package/dist/components/link-card/index.js.map +1 -0
  87. package/dist/components/link-card/link-card.d.ts +62 -0
  88. package/dist/components/link-card/link-card.d.ts.map +1 -0
  89. package/dist/components/link-card/link-card.js +43 -0
  90. package/dist/components/link-card/link-card.js.map +1 -0
  91. package/dist/components/mobile-navigation/index.d.ts +3 -0
  92. package/dist/components/mobile-navigation/index.d.ts.map +1 -0
  93. package/dist/components/mobile-navigation/index.js +2 -0
  94. package/dist/components/mobile-navigation/index.js.map +1 -0
  95. package/dist/components/mobile-navigation/mobile-navigation.d.ts +88 -0
  96. package/dist/components/mobile-navigation/mobile-navigation.d.ts.map +1 -0
  97. package/dist/components/mobile-navigation/mobile-navigation.js +153 -0
  98. package/dist/components/mobile-navigation/mobile-navigation.js.map +1 -0
  99. package/dist/components/navigation-item/index.d.ts +3 -0
  100. package/dist/components/navigation-item/index.d.ts.map +1 -0
  101. package/dist/components/navigation-item/index.js +2 -0
  102. package/dist/components/navigation-item/index.js.map +1 -0
  103. package/dist/components/navigation-item/navigation-item.d.ts +38 -0
  104. package/dist/components/navigation-item/navigation-item.d.ts.map +1 -0
  105. package/dist/components/navigation-item/navigation-item.js +22 -0
  106. package/dist/components/navigation-item/navigation-item.js.map +1 -0
  107. package/dist/components/radio/index.d.ts +3 -0
  108. package/dist/components/radio/index.d.ts.map +1 -0
  109. package/dist/components/radio/index.js +2 -0
  110. package/dist/components/radio/index.js.map +1 -0
  111. package/dist/components/radio/radio.d.ts +58 -0
  112. package/dist/components/radio/radio.d.ts.map +1 -0
  113. package/dist/components/radio/radio.js +37 -0
  114. package/dist/components/radio/radio.js.map +1 -0
  115. package/dist/components/select/index.d.ts +3 -0
  116. package/dist/components/select/index.d.ts.map +1 -0
  117. package/dist/components/select/index.js +2 -0
  118. package/dist/components/select/index.js.map +1 -0
  119. package/dist/components/select/select.d.ts +67 -0
  120. package/dist/components/select/select.d.ts.map +1 -0
  121. package/dist/components/select/select.js +25 -0
  122. package/dist/components/select/select.js.map +1 -0
  123. package/dist/components/select-field/index.d.ts +3 -0
  124. package/dist/components/select-field/index.d.ts.map +1 -0
  125. package/dist/components/select-field/index.js +2 -0
  126. package/dist/components/select-field/index.js.map +1 -0
  127. package/dist/components/select-field/select-field.d.ts +43 -0
  128. package/dist/components/select-field/select-field.d.ts.map +1 -0
  129. package/dist/components/select-field/select-field.js +24 -0
  130. package/dist/components/select-field/select-field.js.map +1 -0
  131. package/dist/components/table/index.d.ts +3 -0
  132. package/dist/components/table/index.d.ts.map +1 -0
  133. package/dist/components/table/index.js +2 -0
  134. package/dist/components/table/index.js.map +1 -0
  135. package/dist/components/table/table.d.ts +251 -0
  136. package/dist/components/table/table.d.ts.map +1 -0
  137. package/dist/components/table/table.js +209 -0
  138. package/dist/components/table/table.js.map +1 -0
  139. package/dist/components/toggle/index.d.ts +3 -0
  140. package/dist/components/toggle/index.d.ts.map +1 -0
  141. package/dist/components/toggle/index.js +2 -0
  142. package/dist/components/toggle/index.js.map +1 -0
  143. package/dist/components/toggle/toggle.d.ts +66 -0
  144. package/dist/components/toggle/toggle.d.ts.map +1 -0
  145. package/dist/components/toggle/toggle.js +49 -0
  146. package/dist/components/toggle/toggle.js.map +1 -0
  147. package/dist/components/tooltip/index.d.ts +3 -0
  148. package/dist/components/tooltip/index.d.ts.map +1 -0
  149. package/dist/components/tooltip/index.js +2 -0
  150. package/dist/components/tooltip/index.js.map +1 -0
  151. package/dist/components/tooltip/tooltip.d.ts +74 -0
  152. package/dist/components/tooltip/tooltip.d.ts.map +1 -0
  153. package/dist/components/tooltip/tooltip.js +180 -0
  154. package/dist/components/tooltip/tooltip.js.map +1 -0
  155. package/dist/index.d.ts +57 -0
  156. package/dist/index.d.ts.map +1 -0
  157. package/dist/index.js +38 -0
  158. package/dist/index.js.map +1 -0
  159. package/dist/internal/development.d.ts +2 -0
  160. package/dist/internal/development.d.ts.map +1 -0
  161. package/dist/internal/development.js +2 -0
  162. package/dist/internal/development.js.map +1 -0
  163. package/dist/internal/field-support.d.ts +68 -0
  164. package/dist/internal/field-support.d.ts.map +1 -0
  165. package/dist/internal/field-support.js +42 -0
  166. package/dist/internal/field-support.js.map +1 -0
  167. package/dist/internal/isomorphic-layout-effect.d.ts +26 -0
  168. package/dist/internal/isomorphic-layout-effect.d.ts.map +1 -0
  169. package/dist/internal/isomorphic-layout-effect.js +26 -0
  170. package/dist/internal/isomorphic-layout-effect.js.map +1 -0
  171. package/dist/styles.css +3474 -0
  172. package/dist/tokens.json +995 -0
  173. package/package.json +76 -0
@@ -0,0 +1,3 @@
1
+ export { FieldGroup } from './field-group.js';
2
+ export type { FieldGroupProps } from './field-group.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/field-group/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,YAAY,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { FieldGroup } from './field-group.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/field-group/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { Input } from './input.js';
2
+ export type { InputProps, InputSize, InputType } from './input.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/input/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { Input } from './input.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/input/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,96 @@
1
+ import type { InputHTMLAttributes } from 'react';
2
+ /** Drawn size. Maps to the `size` axis of the Figma component set. */
3
+ export type InputSize = 'sm' | 'md' | 'lg';
4
+ /**
5
+ * The input types this component's design actually covers.
6
+ *
7
+ * Every one of these renders as a single-line text box, which is what the Figma
8
+ * component draws. The types left out have their own native interface that this
9
+ * styling would fight rather than improve: `checkbox` and `radio` get dedicated
10
+ * Lutra components, `file` renders a button, `range` a slider, `color` a swatch,
11
+ * and `submit`, `button`, `reset` and `image` are buttons — use `Button`.
12
+ * `hidden` renders nothing at all.
13
+ *
14
+ * The date and time types are included because they are text boxes, but browsers
15
+ * add their own picker affordance inside the field — a calendar icon, spin
16
+ * buttons — whose appearance is not ours to control and will not match the Figma
17
+ * drawing exactly. See `input.md`.
18
+ */
19
+ export type InputType = 'text' | 'email' | 'password' | 'search' | 'tel' | 'url' | 'number' | 'date' | 'datetime-local' | 'month' | 'time' | 'week';
20
+ /**
21
+ * Native `<input>` attributes, minus the four this component redefines or refuses.
22
+ *
23
+ * Keeping the rest means `name`, `value`, `defaultValue`, `placeholder`,
24
+ * `required`, `readOnly`, `autoComplete`, `inputMode`, `pattern`, `minLength`,
25
+ * every `on*` handler, every other `aria-*`, and every `data-*` continue to work
26
+ * exactly as they do on a plain `<input>`.
27
+ *
28
+ * - `size` is redefined. The native attribute is a *character count* that sets the
29
+ * field's width; Lutra's is the design system's size scale. They cannot share a
30
+ * name, and the design system's meaning is the one a consumer of this library
31
+ * expects. Width is controlled with CSS or `fullWidth`.
32
+ * - `type` is narrowed to the types this design covers.
33
+ * - `aria-invalid` is removed on purpose — see `invalid`.
34
+ * - `children` is removed because `<input>` is a void element.
35
+ */
36
+ type NativeInputProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'size' | 'type' | 'aria-invalid' | 'children'>;
37
+ export interface InputProps extends NativeInputProps {
38
+ /**
39
+ * Drawn size.
40
+ *
41
+ * `'sm'` sets 14px text. Mobile Safari zooms the page when a focused field's
42
+ * text is below 16px, so prefer `'md'` for anything a person fills in on a
43
+ * phone and keep `'sm'` for dense, secondary UI.
44
+ *
45
+ * @default 'md'
46
+ */
47
+ size?: InputSize;
48
+ /**
49
+ * Marks the value as failing validation.
50
+ *
51
+ * This is the only way to put the field in its error state, and it sets both
52
+ * the visual treatment and `aria-invalid="true"` from the one value. That is
53
+ * deliberate: a field that looks wrong while reporting itself valid is worse
54
+ * than either problem alone, so `aria-invalid` is not accepted as a separate
55
+ * prop and the CSS keys off the ARIA attribute rather than a parallel data
56
+ * attribute. The two cannot disagree.
57
+ *
58
+ * Input owns the invalid *state*. The error *message* belongs to the field
59
+ * wrapper that composes this control, and is connected with `aria-describedby`.
60
+ *
61
+ * @default false
62
+ */
63
+ invalid?: boolean;
64
+ /**
65
+ * Stretches the field to the width of its container. Layout only — it is not a
66
+ * variant and changes nothing else about the input.
67
+ *
68
+ * @default false
69
+ */
70
+ fullWidth?: boolean;
71
+ /** @default 'text' */
72
+ type?: InputType;
73
+ }
74
+ /**
75
+ * A single-line text entry control.
76
+ *
77
+ * Renders a native `<input>`, so text selection, editing shortcuts, autofill,
78
+ * form participation, validation, IME composition, and disabled semantics come
79
+ * from the platform rather than from this component.
80
+ *
81
+ * Hover and focus are browser states expressed in CSS, not props. The only states
82
+ * Input models are the ones a consumer owns: `disabled`, `readOnly` and `invalid`.
83
+ *
84
+ * **Every rendered Input must have an accessible name.** This primitive does not
85
+ * manufacture one, because the name normally comes from a visible `<label>` that
86
+ * the surrounding field composition owns. Until that component exists, associate
87
+ * a real `<label htmlFor>` yourself, or pass `aria-label` / `aria-labelledby`. A
88
+ * `placeholder` is not a name — it disappears on the first keystroke. See
89
+ * `input.md`.
90
+ *
91
+ * `forwardRef` rather than a plain `ref` prop: this package supports React 18.3
92
+ * as well as 19, and ref-as-a-prop only works in 19.
93
+ */
94
+ export declare const Input: import("react").ForwardRefExoticComponent<InputProps & import("react").RefAttributes<HTMLInputElement>>;
95
+ export {};
96
+ //# sourceMappingURL=input.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../../src/components/input/input.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,OAAO,CAAC;AAEjD,sEAAsE;AACtE,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE3C;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,SAAS,GACjB,MAAM,GACN,OAAO,GACP,UAAU,GACV,QAAQ,GACR,KAAK,GACL,KAAK,GACL,QAAQ,GACR,MAAM,GACN,gBAAgB,GAChB,OAAO,GACP,MAAM,GACN,MAAM,CAAC;AAEX;;;;;;;;;;;;;;;GAeG;AACH,KAAK,gBAAgB,GAAG,IAAI,CAC1B,mBAAmB,CAAC,gBAAgB,CAAC,EACrC,MAAM,GAAG,MAAM,GAAG,cAAc,GAAG,UAAU,CAC9C,CAAC;AAEF,MAAM,WAAW,UAAW,SAAQ,gBAAgB;IAClD;;;;;;;;OAQG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,sBAAsB;IACtB,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,KAAK,yGAkBhB,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef } from 'react';
3
+ /**
4
+ * A single-line text entry control.
5
+ *
6
+ * Renders a native `<input>`, so text selection, editing shortcuts, autofill,
7
+ * form participation, validation, IME composition, and disabled semantics come
8
+ * from the platform rather than from this component.
9
+ *
10
+ * Hover and focus are browser states expressed in CSS, not props. The only states
11
+ * Input models are the ones a consumer owns: `disabled`, `readOnly` and `invalid`.
12
+ *
13
+ * **Every rendered Input must have an accessible name.** This primitive does not
14
+ * manufacture one, because the name normally comes from a visible `<label>` that
15
+ * the surrounding field composition owns. Until that component exists, associate
16
+ * a real `<label htmlFor>` yourself, or pass `aria-label` / `aria-labelledby`. A
17
+ * `placeholder` is not a name — it disappears on the first keystroke. See
18
+ * `input.md`.
19
+ *
20
+ * `forwardRef` rather than a plain `ref` prop: this package supports React 18.3
21
+ * as well as 19, and ref-as-a-prop only works in 19.
22
+ */
23
+ export const Input = forwardRef(function Input({ size = 'md', invalid = false, fullWidth = false, type = 'text', className, ...nativeProps }, ref) {
24
+ return (_jsx("input", { ...nativeProps, ref: ref, type: type, className: className === undefined ? 'lutra-input' : `lutra-input ${className}`, "aria-invalid": invalid || undefined, "data-size": size, "data-full-width": fullWidth ? 'true' : undefined }));
25
+ });
26
+ //# sourceMappingURL=input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input.js","sourceRoot":"","sources":["../../../src/components/input/input.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AA8FnC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,UAAU,CAA+B,SAAS,KAAK,CAC1E,EAAE,IAAI,GAAG,IAAI,EAAE,OAAO,GAAG,KAAK,EAAE,SAAS,GAAG,KAAK,EAAE,IAAI,GAAG,MAAM,EAAE,SAAS,EAAE,GAAG,WAAW,EAAE,EAC7F,GAAG;IAEH,OAAO,CACL,mBACM,WAAW,EACf,GAAG,EAAE,GAAG,EACR,IAAI,EAAE,IAAI,EACV,SAAS,EAAE,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,eAAe,SAAS,EAAE,kBAIjE,OAAO,IAAI,SAAS,eACvB,IAAI,qBACE,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,GAC/C,CACH,CAAC;AACJ,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { Link } from './link.js';
2
+ export type { LinkProps, LinkVariant } from './link.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/link/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { Link } from './link.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/link/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,44 @@
1
+ import type { AnchorHTMLAttributes } from 'react';
2
+ /**
3
+ * How the link is drawn. Maps to the `type` axis of the Figma component set, renamed here because
4
+ * `type` is already a native `<a>` attribute and shadowing it would remove something real.
5
+ *
6
+ * `'inline'` is for links inside prose and stays underlined. `'standalone'` is for links that sit on
7
+ * their own, where position and spacing already separate them from surrounding text.
8
+ *
9
+ * **Styling only.** Neither value changes the element, and neither exists to make a link behave like
10
+ * a button.
11
+ */
12
+ export type LinkVariant = 'inline' | 'standalone';
13
+ /**
14
+ * Native `<a>` attributes. Nothing is removed: `href`, `target`, `rel`, `download`, `hrefLang`,
15
+ * `referrerPolicy`, every `aria-*`, every `data-*` and every event handler pass straight through.
16
+ */
17
+ type NativeAnchorProps = AnchorHTMLAttributes<HTMLAnchorElement>;
18
+ export interface LinkProps extends NativeAnchorProps {
19
+ /** @default 'inline' */
20
+ variant?: LinkVariant;
21
+ }
22
+ /**
23
+ * A link. Renders a native `<a>`.
24
+ *
25
+ * **It is always an anchor, and there is no prop that turns it into a button.** The annotation is
26
+ * blunt about why: a link that does not navigate is a button, and a button that navigates is a link.
27
+ * Getting it wrong removes middle-click, open-in-new-tab, copy-link and the browser's own history
28
+ * behaviour — none of which can be added back by hand. If you need an action, use `Button`.
29
+ *
30
+ * Hover, focus, active and visited are browser states expressed in CSS. There are no `hovered`,
31
+ * `focused` or `visited` props: the browser already tracks all four, and a prop would drift out of
32
+ * step with the one that is real.
33
+ *
34
+ * **The link text is the accessible name, and it has to make sense on its own.** Screen-reader users
35
+ * list links out of context, so "Read the accessibility annotation guide" works and "Read more" does
36
+ * not. This component cannot fix a vague label and deliberately does not try.
37
+ *
38
+ * Nothing is appended for `target="_blank"`. The Figma component has no external-link icon and the
39
+ * annotation establishes no "opens in a new tab" contract, so inventing one here would put text in
40
+ * the accessible name that the design system never agreed to. Say it in the link text if it matters.
41
+ */
42
+ export declare const Link: import("react").ForwardRefExoticComponent<LinkProps & import("react").RefAttributes<HTMLAnchorElement>>;
43
+ export {};
44
+ //# sourceMappingURL=link.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../../../src/components/link/link.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAElD;;;;;;;;;GASG;AACH,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,YAAY,CAAC;AAElD;;;GAGG;AACH,KAAK,iBAAiB,GAAG,oBAAoB,CAAC,iBAAiB,CAAC,CAAC;AAEjE,MAAM,WAAW,SAAU,SAAQ,iBAAiB;IAClD,wBAAwB;IACxB,OAAO,CAAC,EAAE,WAAW,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,IAAI,yGAcf,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef } from 'react';
3
+ /**
4
+ * A link. Renders a native `<a>`.
5
+ *
6
+ * **It is always an anchor, and there is no prop that turns it into a button.** The annotation is
7
+ * blunt about why: a link that does not navigate is a button, and a button that navigates is a link.
8
+ * Getting it wrong removes middle-click, open-in-new-tab, copy-link and the browser's own history
9
+ * behaviour — none of which can be added back by hand. If you need an action, use `Button`.
10
+ *
11
+ * Hover, focus, active and visited are browser states expressed in CSS. There are no `hovered`,
12
+ * `focused` or `visited` props: the browser already tracks all four, and a prop would drift out of
13
+ * step with the one that is real.
14
+ *
15
+ * **The link text is the accessible name, and it has to make sense on its own.** Screen-reader users
16
+ * list links out of context, so "Read the accessibility annotation guide" works and "Read more" does
17
+ * not. This component cannot fix a vague label and deliberately does not try.
18
+ *
19
+ * Nothing is appended for `target="_blank"`. The Figma component has no external-link icon and the
20
+ * annotation establishes no "opens in a new tab" contract, so inventing one here would put text in
21
+ * the accessible name that the design system never agreed to. Say it in the link text if it matters.
22
+ */
23
+ export const Link = forwardRef(function Link({ variant = 'inline', className, children, ...nativeProps }, ref) {
24
+ return (_jsx("a", { ...nativeProps, ref: ref, className: className === undefined ? 'lutra-link' : `lutra-link ${className}`, "data-variant": variant, children: children }));
25
+ });
26
+ //# sourceMappingURL=link.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link.js","sourceRoot":"","sources":["../../../src/components/link/link.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AA0BnC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,UAAU,CAA+B,SAAS,IAAI,CACxE,EAAE,OAAO,GAAG,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,WAAW,EAAE,EAC3D,GAAG;IAEH,OAAO,CACL,eACM,WAAW,EACf,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,cAAc,SAAS,EAAE,kBAC/D,OAAO,YAEpB,QAAQ,GACP,CACL,CAAC;AACJ,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { LinkCard } from './link-card.js';
2
+ export type { LinkCardProps } from './link-card.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/link-card/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { LinkCard } from './link-card.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/link-card/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,62 @@
1
+ import type { AnchorHTMLAttributes, ReactNode } from 'react';
2
+ import type { HeadingLevel } from '../card/index.js';
3
+ /**
4
+ * Native `<a>` attributes. They land on the anchor, not on the card, because the anchor is the
5
+ * interactive thing — `href`, `target`, `rel`, `download`, every `aria-*` and every event handler
6
+ * pass through to it.
7
+ */
8
+ type NativeAnchorProps = Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'children'>;
9
+ export interface LinkCardProps extends NativeAnchorProps {
10
+ /** Where the card goes. */
11
+ href: string;
12
+ /** The level the heading renders at. Required, for the same reason it is on `Card`. */
13
+ headingLevel: HeadingLevel;
14
+ /**
15
+ * The link text, which is the whole of the accessible name. It has to make sense read on its own,
16
+ * out of context, because that is how screen-reader users list links.
17
+ */
18
+ children: ReactNode;
19
+ /**
20
+ * Metadata below the link. Deliberately **outside** the accessible name — see below.
21
+ *
22
+ * Because it is outside the name, it must never carry something a person needs in order to know
23
+ * where the link goes.
24
+ */
25
+ supportingText?: ReactNode;
26
+ /** Extra class on the card surface. */
27
+ className?: string;
28
+ }
29
+ /**
30
+ * A card whose whole surface is one destination.
31
+ *
32
+ * ## The shape, and why it is exactly this shape
33
+ *
34
+ * A heading containing a single anchor, with the anchor's `::after` pseudo-element stretched over the
35
+ * card. That produces the two properties the annotation asks for, and it is the only construction
36
+ * that produces both:
37
+ *
38
+ * - **The accessible name is the link text alone.** The anchor wraps only the heading text; the
39
+ * pseudo-element supplies the clickable area. Supporting text and metadata stay out of the name.
40
+ * - **The card is exactly one tab stop.**
41
+ *
42
+ * The two obvious alternatives both fail, and they fail invisibly — the card looks perfect on screen
43
+ * either way:
44
+ *
45
+ * - **An anchor wrapped around the whole card** gives the link a name made of every string inside it,
46
+ * which is unusable in a list of links.
47
+ * - **A `div` with an `onClick`** is not a link at all: no middle-click, no open-in-new-tab, no
48
+ * copy-link, no history, and nothing in the accessibility tree saying it navigates.
49
+ *
50
+ * ## When not to use it
51
+ *
52
+ * Only when the card has exactly one destination and **no other interactive content**. A link inside
53
+ * a link is not valid and the browser will not honour it, and any control under the stretched
54
+ * pseudo-element is unreachable by pointer. If you need two actions, or a link and a button, use
55
+ * `Card` with a `CardFooter`.
56
+ *
57
+ * Hover raises the card and underlines the link text; focus draws the ring. Both change more than
58
+ * colour, so both survive greyscale.
59
+ */
60
+ export declare const LinkCard: import("react").ForwardRefExoticComponent<LinkCardProps & import("react").RefAttributes<HTMLAnchorElement>>;
61
+ export {};
62
+ //# sourceMappingURL=link-card.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-card.d.ts","sourceRoot":"","sources":["../../../src/components/link-card/link-card.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAE7D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD;;;;GAIG;AACH,KAAK,iBAAiB,GAAG,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,UAAU,CAAC,CAAC;AAEnF,MAAM,WAAW,aAAc,SAAQ,iBAAiB;IACtD,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,uFAAuF;IACvF,YAAY,EAAE,YAAY,CAAC;IAC3B;;;OAGG;IACH,QAAQ,EAAE,SAAS,CAAC;IACpB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,SAAS,CAAC;IAC3B,uCAAuC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAmBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,eAAO,MAAM,QAAQ,6GAsBnB,CAAC"}
@@ -0,0 +1,43 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { forwardRef } from 'react';
3
+ /** The chevron from the Figma component. Decorative: the link text is already the name. */
4
+ function ChevronRightIcon() {
5
+ return (_jsx("svg", { className: "lutra-link-card__chevron", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", "aria-hidden": "true", focusable: "false", children: _jsx("path", { d: "M9 5l7 7-7 7", strokeLinecap: "round", strokeLinejoin: "round" }) }));
6
+ }
7
+ /**
8
+ * A card whose whole surface is one destination.
9
+ *
10
+ * ## The shape, and why it is exactly this shape
11
+ *
12
+ * A heading containing a single anchor, with the anchor's `::after` pseudo-element stretched over the
13
+ * card. That produces the two properties the annotation asks for, and it is the only construction
14
+ * that produces both:
15
+ *
16
+ * - **The accessible name is the link text alone.** The anchor wraps only the heading text; the
17
+ * pseudo-element supplies the clickable area. Supporting text and metadata stay out of the name.
18
+ * - **The card is exactly one tab stop.**
19
+ *
20
+ * The two obvious alternatives both fail, and they fail invisibly — the card looks perfect on screen
21
+ * either way:
22
+ *
23
+ * - **An anchor wrapped around the whole card** gives the link a name made of every string inside it,
24
+ * which is unusable in a list of links.
25
+ * - **A `div` with an `onClick`** is not a link at all: no middle-click, no open-in-new-tab, no
26
+ * copy-link, no history, and nothing in the accessibility tree saying it navigates.
27
+ *
28
+ * ## When not to use it
29
+ *
30
+ * Only when the card has exactly one destination and **no other interactive content**. A link inside
31
+ * a link is not valid and the browser will not honour it, and any control under the stretched
32
+ * pseudo-element is unreachable by pointer. If you need two actions, or a link and a button, use
33
+ * `Card` with a `CardFooter`.
34
+ *
35
+ * Hover raises the card and underlines the link text; focus draws the ring. Both change more than
36
+ * colour, so both survive greyscale.
37
+ */
38
+ export const LinkCard = forwardRef(function LinkCard({ href, headingLevel, children, supportingText, className, ...nativeProps }, ref) {
39
+ const Heading = `h${headingLevel}`;
40
+ const hasSupportingText = supportingText !== undefined && supportingText !== null && supportingText !== false;
41
+ return (_jsxs("div", { className: className === undefined ? 'lutra-link-card' : `lutra-link-card ${className}`, children: [_jsxs(Heading, { className: "lutra-link-card__heading", children: [_jsx("a", { ...nativeProps, ref: ref, className: "lutra-link-card__link", href: href, children: children }), _jsx(ChevronRightIcon, {})] }), hasSupportingText && _jsx("p", { className: "lutra-link-card__supporting-text", children: supportingText })] }));
42
+ });
43
+ //# sourceMappingURL=link-card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-card.js","sourceRoot":"","sources":["../../../src/components/link-card/link-card.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAiCnC,2FAA2F;AAC3F,SAAS,gBAAgB;IACvB,OAAO,CACL,cACE,SAAS,EAAC,0BAA0B,EACpC,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,GAAG,iBACH,MAAM,EAClB,SAAS,EAAC,OAAO,YAEjB,eAAM,CAAC,EAAC,cAAc,EAAC,aAAa,EAAC,OAAO,EAAC,cAAc,EAAC,OAAO,GAAG,GAClE,CACP,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,UAAU,CAAmC,SAAS,QAAQ,CACpF,EAAE,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,SAAS,EAAE,GAAG,WAAW,EAAE,EAC3E,GAAG;IAEH,MAAM,OAAO,GAAG,IAAI,YAAY,EAAW,CAAC;IAC5C,MAAM,iBAAiB,GACrB,cAAc,KAAK,SAAS,IAAI,cAAc,KAAK,IAAI,IAAI,cAAc,KAAK,KAAK,CAAC;IAEtF,OAAO,CACL,eAAK,SAAS,EAAE,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,mBAAmB,SAAS,EAAE,aAC1F,MAAC,OAAO,IAAC,SAAS,EAAC,0BAA0B,aAG3C,eAAO,WAAW,EAAE,GAAG,EAAE,GAAG,EAAE,SAAS,EAAC,uBAAuB,EAAC,IAAI,EAAE,IAAI,YACvE,QAAQ,GACP,EACJ,KAAC,gBAAgB,KAAG,IACZ,EAET,iBAAiB,IAAI,YAAG,SAAS,EAAC,kCAAkC,YAAE,cAAc,GAAK,IACtF,CACP,CAAC;AACJ,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { MobileNavigation } from './mobile-navigation.js';
2
+ export type { MobileNavigationProps, MobileNavigationAlign } from './mobile-navigation.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/mobile-navigation/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,YAAY,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { MobileNavigation } from './mobile-navigation.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/mobile-navigation/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,88 @@
1
+ import type { ReactNode } from 'react';
2
+ /**
3
+ * Which edge of the trigger the panel is anchored to. Styling and position only — it changes no
4
+ * semantics. Named to match `Tooltip`'s `align`, so the library has one word for one idea.
5
+ */
6
+ export type MobileNavigationAlign = 'start' | 'end';
7
+ export interface MobileNavigationProps {
8
+ /**
9
+ * The trigger's accessible name. Say what it controls — the annotation asks for a name that names
10
+ * the thing being opened, not a shape.
11
+ *
12
+ * @default 'Menu'
13
+ */
14
+ label?: string;
15
+ /**
16
+ * The name of the navigation landmark inside the panel.
17
+ *
18
+ * A page may hold more than one `nav`, and when it does each needs its own name so they can be
19
+ * told apart in a landmark list.
20
+ *
21
+ * @default 'Primary'
22
+ */
23
+ navLabel?: string;
24
+ /** Accessible name of the close control inside the panel. @default 'Close navigation' */
25
+ closeLabel?: string;
26
+ /** The destinations. `NavigationItem`s. */
27
+ children: ReactNode;
28
+ /** Controlled open state. Pair with `onOpenChange`. */
29
+ open?: boolean;
30
+ /** Uncontrolled initial state. @default false */
31
+ defaultOpen?: boolean;
32
+ /** Called whenever the component wants the open state to change. */
33
+ onOpenChange?: (open: boolean) => void;
34
+ /**
35
+ * Makes the rest of the page `inert` while the panel is open, so nothing behind it can be
36
+ * focused, clicked or read.
37
+ *
38
+ * Off by default, and the reasoning is in `mobile-navigation.md`: the accessibility annotation
39
+ * asks for containment, the page guidance says not to trap focus, and `inert` is the one
40
+ * mechanism that satisfies both — but it also stops an outside press from dismissing the panel,
41
+ * because inert content receives no events. Turning it on is therefore a product decision about a
42
+ * specific panel, not a default this component may make on everyone's behalf.
43
+ *
44
+ * @default false
45
+ */
46
+ containBackground?: boolean;
47
+ /**
48
+ * Which edge of the trigger the panel is anchored to, using the same vocabulary as `Tooltip`.
49
+ *
50
+ * `'start'` anchors the panel's inline start to the trigger's, which is right when the trigger sits
51
+ * at the start of its row. Pass `'end'` when the trigger sits at the **end** of a row — a site
52
+ * header with the site name on one side and the menu on the other, which is what the Figma
53
+ * `Site Header` draws. A start-anchored panel there extends past the viewport edge and forces
54
+ * horizontal scrolling.
55
+ *
56
+ * @default 'start'
57
+ */
58
+ align?: MobileNavigationAlign;
59
+ /** Extra class on the wrapper. */
60
+ className?: string;
61
+ }
62
+ /**
63
+ * The mobile primary-navigation disclosure: a button that reveals a panel of destinations.
64
+ *
65
+ * **It is a disclosure, not a dialog.** The annotation asks for a button carrying `aria-expanded`
66
+ * and `aria-controls`, and the page guidance says not to apply dialog semantics or a focus trap. So
67
+ * there is no `role="dialog"`, no `aria-modal`, and no focus-trap loop. The panel is a `nav`
68
+ * landmark with its own accessible name, holding ordinary anchors.
69
+ *
70
+ * Behaviour, all of it from the annotation:
71
+ *
72
+ * - The trigger is a real button, so Enter and Space open it.
73
+ * - `aria-expanded` reflects the state; `aria-controls` points at the panel.
74
+ * - Opening moves focus into the panel.
75
+ * - Escape closes it and returns focus to the trigger.
76
+ * - The close button and a pointer press outside also close it, and Escape and the close button both
77
+ * return focus to the trigger.
78
+ * - Every target holds the 44px minimum; that is in the stylesheet.
79
+ *
80
+ * The one place the two Figma sources disagree is background containment, and that is resolved by
81
+ * `containBackground` — see the prop, and `mobile-navigation.md` for the full argument.
82
+ *
83
+ * Figma's `Mobile Navigation` component draws the open panel only; the trigger lives in the header
84
+ * pattern. It is owned here because `aria-expanded`, `aria-controls` and the focus round trip are one
85
+ * mechanism, and splitting them across two components would leave neither able to keep the contract.
86
+ */
87
+ export declare function MobileNavigation({ label, navLabel, closeLabel, children, open: controlledOpen, defaultOpen, onOpenChange, containBackground, align, className, }: MobileNavigationProps): import("react").JSX.Element;
88
+ //# sourceMappingURL=mobile-navigation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mobile-navigation.d.ts","sourceRoot":"","sources":["../../../src/components/mobile-navigation/mobile-navigation.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIvC;;;GAGG;AACH,MAAM,MAAM,qBAAqB,GAAG,OAAO,GAAG,KAAK,CAAC;AAEpD,MAAM,WAAW,qBAAqB;IACpC;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yFAAyF;IACzF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2CAA2C;IAC3C,QAAQ,EAAE,SAAS,CAAC;IACpB,uDAAuD;IACvD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,iDAAiD;IACjD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,oEAAoE;IACpE,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC;;;;;;;;;;;OAWG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;;;;;;OAUG;IACH,KAAK,CAAC,EAAE,qBAAqB,CAAC;IAC9B,kCAAkC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA+CD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,KAAc,EACd,QAAoB,EACpB,UAA+B,EAC/B,QAAQ,EACR,IAAI,EAAE,cAAc,EACpB,WAAmB,EACnB,YAAY,EACZ,iBAAyB,EACzB,KAAe,EACf,SAAS,GACV,EAAE,qBAAqB,+BAiJvB"}