@sakura-ui/sakura-ui 0.4.1 → 0.5.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 (38) hide show
  1. package/README.md +87 -0
  2. package/package.json +3 -3
  3. package/packages/core/package.json +1 -1
  4. package/packages/core/src/components/Card.tsx +51 -32
  5. package/packages/core/src/components/Faq.tsx +8 -24
  6. package/packages/core/src/components/LinkCard.tsx +103 -54
  7. package/packages/core/src/components/index.ts +1 -0
  8. package/packages/core/src/index.ts +2 -0
  9. package/packages/core/tests/Card.test.tsx +124 -20
  10. package/packages/core/tests/Faq.test.tsx +75 -0
  11. package/packages/core/tests/LinkCard.test.tsx +151 -0
  12. package/packages/forms/.turbo/turbo-build.log +3 -3
  13. package/packages/forms/dist/index.cjs.js +18 -18
  14. package/packages/forms/dist/index.es.js +166 -178
  15. package/packages/forms/dist/types/components/Checkbox.d.ts.map +1 -1
  16. package/packages/forms/dist/types/components/FileInput.d.ts.map +1 -1
  17. package/packages/forms/dist/types/components/Input.d.ts.map +1 -1
  18. package/packages/forms/dist/types/components/Radio.d.ts.map +1 -1
  19. package/packages/forms/dist/types/components/Select.d.ts.map +1 -1
  20. package/packages/forms/dist/types/components/Textarea.d.ts.map +1 -1
  21. package/packages/forms/package.json +1 -1
  22. package/packages/forms/src/components/Checkbox.tsx +0 -1
  23. package/packages/forms/src/components/FileInput.tsx +3 -8
  24. package/packages/forms/src/components/Input.tsx +0 -1
  25. package/packages/forms/src/components/Radio.tsx +0 -3
  26. package/packages/forms/src/components/Select.tsx +0 -1
  27. package/packages/forms/src/components/Textarea.tsx +0 -1
  28. package/packages/forms/tests/FileInput.test.tsx +30 -5
  29. package/packages/helper/.turbo/turbo-build.log +1 -1
  30. package/packages/markdown/.turbo/turbo-build.log +4 -4
  31. package/packages/markdown/dist/index.cjs.js +23 -23
  32. package/packages/markdown/dist/index.es.js +1732 -1727
  33. package/packages/markdown/dist/types/components/Markdown.d.ts.map +1 -1
  34. package/packages/markdown/dist/types/plugins/card.d.ts.map +1 -1
  35. package/packages/markdown/package.json +3 -2
  36. package/packages/markdown/src/components/Markdown.tsx +35 -11
  37. package/packages/markdown/src/plugins/card.ts +14 -0
  38. package/packages/tailwind-theme-plugin/.turbo/turbo-build.log +1 -1
package/README.md CHANGED
@@ -25,6 +25,13 @@ $ pnpm add @sakura-ui/core @sakura-ui/forms @sakura-ui/tailwind-theme-plugin @sa
25
25
  $ pnpm add @sakura-ui/core@0.3.1 @sakura-ui/forms@0.2.2 @sakura-ui/tailwind-theme-plugin@0.2.2 @sakura-ui/markdown@0.0.17
26
26
  ```
27
27
 
28
+ ### For the Card API before 0.5.0
29
+ `Card` and `LinkCard` changed shape in `@sakura-ui/core` 0.5.0. See
30
+ [Card and LinkCard](#card-and-linkcard) for what to change; to stay on the old API for now:
31
+ ```
32
+ $ pnpm add @sakura-ui/core@0.4.1 @sakura-ui/markdown@0.2.2
33
+ ```
34
+
28
35
  ## Configuration
29
36
  tailwind.config.js
30
37
  ```ts
@@ -103,6 +110,40 @@ export default App
103
110
  - Link
104
111
  - Card
105
112
  - LinkCard
113
+
114
+ ### Card and LinkCard
115
+ `CardHeader` needs an `as` property. There is no default: the right level depends on the
116
+ document outline around the card, and a wrong guess breaks heading navigation. Use `p` for
117
+ lists of many cards, where headings would only add noise.
118
+
119
+ ```tsx
120
+ <Card>
121
+ <CardHeader as="h3">Title</CardHeader>
122
+ <CardBody>Body</CardBody>
123
+ </Card>
124
+ ```
125
+
126
+ `LinkCard` puts the link in the title, and the link covers the whole card. Because of that a
127
+ link card cannot contain another link, a button or a form control.
128
+
129
+ ```tsx
130
+ <LinkCard>
131
+ <LinkCardHeader as="h3" href="/readme">Title</LinkCardHeader>
132
+ <CardBody>Body</CardBody>
133
+ <LinkCardFooter>June 27th, 2026</LinkCardFooter>
134
+ </LinkCard>
135
+ ```
136
+
137
+ `Card` renders a `div` and carries no ARIA of its own, so nothing can dangle or collide. A
138
+ `div` maps to the generic role, which cannot take an accessible name. When a card really is a
139
+ self-contained composition, name it yourself:
140
+
141
+ ```tsx
142
+ <Card as="article" aria-labelledby="card-title">
143
+ <CardHeader as="h3" id="card-title">Title</CardHeader>
144
+ <CardBody>Body</CardBody>
145
+ </Card>
146
+ ```
106
147
  - Ol
107
148
  - Ul
108
149
  - Table
@@ -128,9 +169,55 @@ export default App
128
169
  - LabelControl
129
170
  - FieldsetControl
130
171
 
172
+ Every form control takes its accessible name from the label it is associated with, so wrap it in `LabelControl` — or `FieldsetControl` for a group of radios or checkboxes. A control rendered without a label has no name for assistive technology.
173
+
174
+ ```tsx
175
+ <LabelControl labelText="添付書類" isRequired>
176
+ <FileInput />
177
+ </LabelControl>
178
+ ```
179
+
180
+ If a visible label is not an option, pass `aria-label` yourself; it is forwarded to the underlying element.
181
+
131
182
  ## Markdwon extension
132
183
  - Markdown
133
184
 
185
+ ## Accessible names
186
+
187
+ A note for anyone working on these components, rather than for people using them.
188
+
189
+ Whether an element can be named at all is decided by its role, not by the attribute
190
+ you write. Every ARIA role declares where its name may come from:
191
+
192
+ | Name from | Meaning | Roles |
193
+ |---|---|---|
194
+ | author, contents | `aria-label`, `aria-labelledby`, **or the text inside** | `button`, `link`, `heading`, `cell`, `option`, `tab`, `menuitem` |
195
+ | author | `aria-label`, `aria-labelledby` or `title` **only** | `article`, `region`, `dialog`, `img`, `table`, `form` |
196
+ | prohibited | cannot be named; the attribute is ignored | `generic` (a bare `<div>` or `<span>`), `paragraph` |
197
+
198
+ Two consequences catch people out.
199
+
200
+ **A `<div>` cannot be given a name.** It maps to `generic`, where naming is prohibited,
201
+ so `<div aria-label="Card">` does nothing at all. Reaching for a name means reaching for
202
+ a different element, which is why dropping `aria-labelledby` from `Card` and dropping its
203
+ `<article>` were the same decision.
204
+
205
+ **An `<article>` does not take its name from a heading inside it.** It is named by the
206
+ author only, so `<article><h3>Title</h3></article>` has no accessible name. A `<button>`
207
+ or an `<a>` would be named "Title" here, because those are named from their contents.
208
+ Mixing the two up is what made `LinkCard` produce a link with no name: an `<a>` wrapping
209
+ an `<article>` never reached the title, because Chrome does not descend into a role that
210
+ is named by its author.
211
+
212
+ So, in practice:
213
+
214
+ - Leave it a `<div>` and give it no name. A list and the headings inside it already tell
215
+ the reader where they are.
216
+ - Use `<section>` only with a name. A `<section>` becomes a `region` **because** it has one;
217
+ without a name it is no different from a `<div>`.
218
+ - Use `<article>` only for something that stands on its own, and name it when you do.
219
+ Naming it is recommended by the APG so that readers can tell one article from the next.
220
+
134
221
  ## Development
135
222
 
136
223
  ### Setup
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sakura-ui/sakura-ui",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "keywords": [],
5
5
  "author": "glassonion1",
6
6
  "homepage": "https://github.com/glassonion1/sakura-ui",
@@ -19,9 +19,9 @@
19
19
  "pnpm": ">=9.15.1"
20
20
  },
21
21
  "dependencies": {
22
- "@sakura-ui/core": "0.4.1",
22
+ "@sakura-ui/forms": "0.4.2",
23
+ "@sakura-ui/core": "0.5.0",
23
24
  "@sakura-ui/helper": "0.0.9",
24
- "@sakura-ui/forms": "0.4.1",
25
25
  "@sakura-ui/tailwind-theme-plugin": "0.4.1"
26
26
  },
27
27
  "peerDependencies": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sakura-ui/core",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "",
5
5
  "keywords": [
6
6
  "react",
@@ -1,21 +1,16 @@
1
1
  import React from 'react'
2
2
  import { cx } from '@sakura-ui/helper'
3
3
 
4
- interface IdContextType {
5
- id: string
6
- }
7
-
8
- const IdContext = React.createContext<IdContextType>({ id: '' })
9
-
10
4
  export namespace Card {
11
- export interface Props extends React.ComponentPropsWithoutRef<'article'> {}
5
+ export interface Props<T extends React.ElementType> {
6
+ as?: T
7
+ }
12
8
  }
13
9
 
14
- export const Card = (props: Card.Props) => {
15
- const { className, children, ...restProps } = props
16
-
17
- const id = React.useId()
18
- const ctx: IdContextType = { id: id }
10
+ export const Card = <T extends React.ElementType = 'div'>(
11
+ props: Card.Props<T> & Omit<React.ComponentProps<T>, keyof Card.Props<T>>
12
+ ) => {
13
+ const { as: Component = 'div', className, children, ...restProps } = props
19
14
 
20
15
  const style = `
21
16
  border
@@ -27,20 +22,17 @@ export const Card = (props: Card.Props) => {
27
22
  overflow-hidden
28
23
  `
29
24
 
25
+ // A div cannot carry an accessible name, so no ARIA is set here. See the
26
+ // Accessible names section of the README before adding any.
30
27
  return (
31
- <IdContext value={ctx}>
32
- <article
33
- aria-labelledby={ctx.id}
34
- aria-describedby={`${ctx.id}-desc`}
35
- className={cx(style, className)}
36
- {...restProps}
37
- >
38
- {children}
39
- </article>
40
- </IdContext>
28
+ <Component className={cx(style, className)} {...restProps}>
29
+ {children}
30
+ </Component>
41
31
  )
42
32
  }
43
33
 
34
+ Card.displayName = 'Card'
35
+
44
36
  export namespace CardImg {
45
37
  export interface Props extends React.ComponentPropsWithoutRef<'img'> {}
46
38
  }
@@ -57,16 +49,37 @@ export const CardImg = (props: CardImg.Props) => {
57
49
  return <img className={cx(style, className)} {...restProps} />
58
50
  }
59
51
 
52
+ CardImg.displayName = 'CardImg'
53
+
54
+ export type CardHeaderAs = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p'
55
+
60
56
  export namespace CardHeader {
61
- export interface Props extends React.ComponentPropsWithoutRef<'div'> {}
57
+ export interface Props extends React.ComponentPropsWithoutRef<'h3'> {
58
+ /**
59
+ * The element to render the title as. Required on purpose: the right level
60
+ * depends on the surrounding document outline, so the library must not pick
61
+ * one. Use 'p' for lists of many cards, where headings would add noise.
62
+ */
63
+ as: CardHeaderAs
64
+ }
62
65
  }
63
66
 
64
67
  export const CardHeader = (props: CardHeader.Props) => {
65
- const { className, children, ...restProps } = props
66
-
67
- const ctx = React.useContext(IdContext)
68
-
68
+ const { as: Component, className, children, ...restProps } = props
69
+
70
+ if (!Component) {
71
+ // Without this, React reports an invalid element type and suggests a missing
72
+ // export, which says nothing about the property that was actually left out.
73
+ throw new Error(
74
+ 'CardHeader: the "as" property is required. Pass the heading level that ' +
75
+ 'fits the outline around the card, or "p" for lists of many cards.'
76
+ )
77
+ }
78
+
79
+ // Reset the browser defaults of the heading and paragraph elements so that the
80
+ // rendered element does not change how the card looks.
69
81
  const style = `
82
+ m-0
70
83
  text-base
71
84
  leading-[2rem]
72
85
  font-medium
@@ -77,12 +90,14 @@ export const CardHeader = (props: CardHeader.Props) => {
77
90
  `
78
91
 
79
92
  return (
80
- <div id={ctx.id} className={cx(style, className)} {...restProps}>
93
+ <Component className={cx(style, className)} {...restProps}>
81
94
  {children}
82
- </div>
95
+ </Component>
83
96
  )
84
97
  }
85
98
 
99
+ CardHeader.displayName = 'CardHeader'
100
+
86
101
  export namespace CardBody {
87
102
  export interface Props extends React.ComponentPropsWithoutRef<'div'> {}
88
103
  }
@@ -90,8 +105,6 @@ export namespace CardBody {
90
105
  export const CardBody = (props: CardBody.Props) => {
91
106
  const { className, children, ...restProps } = props
92
107
 
93
- const ctx = React.useContext(IdContext)
94
-
95
108
  const style = `
96
109
  text-base-sm
97
110
  leading-[1.85rem]
@@ -101,13 +114,17 @@ export const CardBody = (props: CardBody.Props) => {
101
114
  px-6
102
115
  `
103
116
 
117
+ // No generated id here. Callers that want the body to describe the card pass
118
+ // their own id and point aria-describedby at it.
104
119
  return (
105
- <div id={`${ctx.id}-desc`} className={cx(style, className)} {...restProps}>
120
+ <div className={cx(style, className)} {...restProps}>
106
121
  {children}
107
122
  </div>
108
123
  )
109
124
  }
110
125
 
126
+ CardBody.displayName = 'CardBody'
127
+
111
128
  export namespace CardFooter {
112
129
  export interface Props extends React.ComponentPropsWithoutRef<'div'> {}
113
130
  }
@@ -135,3 +152,5 @@ export const CardFooter = (props: CardFooter.Props) => {
135
152
  </div>
136
153
  )
137
154
  }
155
+
156
+ CardFooter.displayName = 'CardFooter'
@@ -18,18 +18,12 @@ export const Faq = (props: Faq.Props) => {
18
18
  flex-col
19
19
  gap-8
20
20
  `
21
+ // No schema.org FAQPage markup here. Google discontinued the FAQ rich result
22
+ // in May 2026, so it is not implemented.
21
23
  return (
22
- <article itemScope itemType="https://schema.org/FAQPage">
23
- <dl
24
- className={cx(style, className)}
25
- itemScope
26
- itemProp="mainEntity"
27
- itemType="https://schema.org/Question"
28
- {...restProps}
29
- >
30
- {children}
31
- </dl>
32
- </article>
24
+ <dl className={cx(style, className)} {...restProps}>
25
+ {children}
26
+ </dl>
33
27
  )
34
28
  }
35
29
 
@@ -47,11 +41,7 @@ export const Question = (props: Question.Props) => {
47
41
  mt-8
48
42
  `
49
43
  return (
50
- <dt
51
- className={cx(style, headingStyle, className)}
52
- itemProp="name"
53
- {...restProps}
54
- >
44
+ <dt className={cx(style, headingStyle, className)} {...restProps}>
55
45
  <span aria-hidden="true">Q</span>
56
46
  <span>{children}</span>
57
47
  </dt>
@@ -72,17 +62,11 @@ export const Answer = (props: Answer.Props) => {
72
62
  gap-8
73
63
  `
74
64
  return (
75
- <dd
76
- className={cx(style, className)}
77
- itemScope
78
- itemProp="acceptedAnswer"
79
- itemType="https://schema.org/Answer"
80
- {...restProps}
81
- >
65
+ <dd className={cx(style, className)} {...restProps}>
82
66
  <span className={cx(headingStyle, '!leading-none')} aria-hidden="true">
83
67
  A
84
68
  </span>
85
- <span itemProp="text">{children}</span>
69
+ <span>{children}</span>
86
70
  </dd>
87
71
  )
88
72
  }
@@ -1,98 +1,145 @@
1
1
  import React from 'react'
2
- import { cx, Style } from '@sakura-ui/helper'
3
- import { Card, CardFooter, CardHeader } from './Card'
2
+ import { cx } from '@sakura-ui/helper'
3
+ import { Card, CardFooter, CardHeader, type CardHeaderAs } from './Card'
4
4
  import { Icon } from './Icon'
5
5
 
6
6
  export namespace LinkCard {
7
- export interface Props<T extends React.ElementType>
8
- extends React.ComponentProps<'article'> {
9
- href: string
10
- target?: string
11
- linkAs?: T
12
- }
7
+ export interface Props<T extends React.ElementType> extends Card.Props<T> {}
13
8
  }
14
9
 
15
- export const LinkCard = <T extends React.ElementType = 'a'>(
16
- props: LinkCard.Props<T>
10
+ /**
11
+ * A Card whose title carries the link. The link stretches over the whole card
12
+ * through a ::after overlay, so the card cannot contain another link, a button
13
+ * or a form control.
14
+ */
15
+ export const LinkCard = <T extends React.ElementType = 'div'>(
16
+ props: LinkCard.Props<T> & Omit<React.ComponentProps<T>, keyof LinkCard.Props<T>>
17
17
  ) => {
18
- const {
19
- href,
20
- target,
21
- linkAs: Component = 'a',
22
- className,
23
- children,
24
- ...rest
25
- } = props
18
+ const { className, children, ...restProps } = props
26
19
 
27
- const styleLink = `
28
- grid
29
- outline-offset-4
30
- rounded-2xl
31
- sm:rounded-3xl
32
- w-full h-full
33
- ${Style.focusCard}
20
+ // z-0 establishes a stacking context so that the overlay of the title can be
21
+ // raised above the rest of the card. CardFooter is sticky, which makes it a
22
+ // positioned element that would otherwise paint over the overlay and swallow
23
+ // the clicks aimed at the bottom of the card.
24
+ const stylePosition = `
25
+ relative
26
+ z-0
34
27
  group
35
28
  `
36
29
 
37
30
  const styleHover = `
38
- group-hover:outline
39
- group-hover:outline-blue-900
40
- group-hover:outline-4
41
- group-hover:outline-offset-[-1px]
31
+ hover:outline
32
+ hover:outline-2
33
+ hover:outline-black
34
+ hover:bg-solid-gray-50
42
35
  `
43
36
 
37
+ // The focus ring goes on the card itself, not on the <a>, so that it surrounds
38
+ // the whole card. The card sets overflow-hidden, which would cut off a ring
39
+ // drawn on the ::after overlay inside it. An element never clips its own
40
+ // outline, so the ring survives here.
41
+ const styleFocus = `
42
+ has-[a:focus-visible]:outline
43
+ has-[a:focus-visible]:outline-4
44
+ has-[a:focus-visible]:outline-black
45
+ has-[a:focus-visible]:outline-offset-[calc(2/16*1rem)]
46
+ has-[a:focus-visible]:ring-[calc(2/16*1rem)]
47
+ has-[a:focus-visible]:ring-yellow-300
48
+ `
49
+
50
+ // TypeScript cannot carry the generic through to Card, which is polymorphic in
51
+ // the same way. The public signature above stays exact; only this hand-off is
52
+ // widened.
53
+ const Root = Card as React.ElementType
54
+
44
55
  return (
45
- <Component className={cx(styleLink)} href={href} target={target}>
46
- <Card className={cx(styleHover, className)} {...rest}>
47
- {children}
48
- </Card>
49
- </Component>
56
+ <Root
57
+ className={cx(stylePosition, styleHover, styleFocus, className)}
58
+ {...restProps}
59
+ >
60
+ {children}
61
+ </Root>
50
62
  )
51
63
  }
52
64
 
65
+ LinkCard.displayName = 'LinkCard'
66
+
53
67
  export namespace LinkCardHeader {
54
- export interface Props extends React.ComponentProps<'div'> {
55
- target?: string
68
+ export interface Props<T extends React.ElementType> {
69
+ /** The element to render the title as. See CardHeader. */
70
+ as: CardHeaderAs
71
+ /** The component to render the link as. Defaults to an anchor element. */
72
+ linkAs?: T
56
73
  }
57
74
  }
58
75
 
59
- export const LinkCardHeader = ({
60
- target,
61
- className,
62
- children
63
- }: LinkCardHeader.Props) => {
76
+ /**
77
+ * The title of a LinkCard, holding the link that covers the whole card.
78
+ *
79
+ * `className` styles the title element; every other prop (href, target, and
80
+ * whatever the component passed to `linkAs` accepts) goes to the link.
81
+ */
82
+ export const LinkCardHeader = <T extends React.ElementType = 'a'>(
83
+ props: LinkCardHeader.Props<T> &
84
+ Omit<React.ComponentProps<T>, keyof LinkCardHeader.Props<T>>
85
+ ) => {
86
+ const {
87
+ as,
88
+ linkAs: Component = 'a',
89
+ className,
90
+ children,
91
+ ...restProps
92
+ } = props
93
+
94
+ if (!as) {
95
+ throw new Error(
96
+ 'LinkCardHeader: the "as" property is required. Pass the heading level ' +
97
+ 'that fits the outline around the card, or "p" for lists of many cards.'
98
+ )
99
+ }
100
+
64
101
  const styleHeading = `
65
102
  decoration-blue-900
66
103
  underline
67
104
  underline-offset-[calc(3/16*1rem)]
68
- `
69
- const styleHover = `
105
+
70
106
  group-hover:text-blue-900
71
107
  group-hover:decoration-[calc(3/16*1rem)]
72
108
  `
73
109
 
110
+ // The ::after overlay makes the whole card clickable. It carries no visual of
111
+ // its own; the focus ring lives on the LinkCard element.
112
+ const styleOverlay = `
113
+ after:content-['']
114
+ after:absolute
115
+ after:inset-0
116
+ after:z-10
117
+ focus-visible:outline-none
118
+ `
119
+
74
120
  return (
75
- <CardHeader className={cx(className, styleHeading, styleHover)}>
76
- <span>
121
+ <CardHeader as={as} className={cx(className, styleHeading)}>
122
+ <Component className={styleOverlay} {...restProps}>
77
123
  {children}
78
- {target === '_blank' && (
79
- <Icon opticalSize={16} className="ml-1">
124
+ {restProps.target === '_blank' && (
125
+ <Icon opticalSize={16} className="ml-1" altText="新しいタブで開きます">
80
126
  open_in_new
81
127
  </Icon>
82
128
  )}
83
- </span>
129
+ </Component>
84
130
  </CardHeader>
85
131
  )
86
132
  }
87
133
 
134
+ LinkCardHeader.displayName = 'LinkCardHeader'
135
+
88
136
  export namespace LinkCardFooter {
89
- export interface Props extends React.ComponentProps<'div'> {}
137
+ export interface Props extends React.ComponentPropsWithoutRef<'div'> {}
90
138
  }
91
139
 
92
- export const LinkCardFooter = ({
93
- className,
94
- children
95
- }: LinkCardFooter.Props) => {
140
+ export const LinkCardFooter = (props: LinkCardFooter.Props) => {
141
+ const { className, children, ...restProps } = props
142
+
96
143
  const style = `
97
144
  flex
98
145
  justify-between
@@ -116,7 +163,7 @@ export const LinkCardFooter = ({
116
163
  `
117
164
 
118
165
  return (
119
- <CardFooter className={cx(className, style)}>
166
+ <CardFooter className={cx(style, className)} {...restProps}>
120
167
  <span>{children}</span>
121
168
  <span className={cx(styleIcon, styleIconHover)}>
122
169
  <Icon opticalSize={16}>arrow_forward</Icon>
@@ -124,3 +171,5 @@ export const LinkCardFooter = ({
124
171
  </CardFooter>
125
172
  )
126
173
  }
174
+
175
+ LinkCardFooter.displayName = 'LinkCardFooter'
@@ -1,6 +1,7 @@
1
1
  export { Button } from './Button'
2
2
  export { MenuButton } from './MenuButton'
3
3
  export { Card, CardImg, CardHeader, CardBody, CardFooter } from './Card'
4
+ export type { CardHeaderAs } from './Card'
4
5
  export { LinkCard, LinkCardHeader, LinkCardFooter } from './LinkCard'
5
6
  export { Code } from './Code'
6
7
  export { Faq, Question, Answer } from './Faq'
@@ -38,3 +38,5 @@ export {
38
38
  PopoverMenu,
39
39
  PopoverMenuItem
40
40
  } from './components'
41
+
42
+ export type { CardHeaderAs } from './components'