@forumone/throughline-reference-ds 0.0.1 → 0.2.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.
- package/CHANGELOG.md +7 -0
- package/LICENSE +21 -0
- package/README.md +102 -28
- package/dist/components/CTASection/CTASection.contract.d.ts +3 -0
- package/dist/components/CTASection/CTASection.contract.d.ts.map +1 -0
- package/dist/components/CTASection/CTASection.contract.js +117 -0
- package/dist/components/CTASection/CTASection.contract.js.map +1 -0
- package/dist/components/CTASection/CTASection.d.ts +17 -0
- package/dist/components/CTASection/CTASection.d.ts.map +1 -0
- package/dist/components/CTASection/CTASection.js +7 -0
- package/dist/components/CTASection/CTASection.js.map +1 -0
- package/dist/components/CTASection/index.d.ts +3 -0
- package/dist/components/CTASection/index.d.ts.map +1 -0
- package/dist/components/CTASection/index.js +3 -0
- package/dist/components/CTASection/index.js.map +1 -0
- package/dist/components/Card/Card.contract.d.ts +3 -0
- package/dist/components/Card/Card.contract.d.ts.map +1 -0
- package/dist/components/Card/Card.contract.js +111 -0
- package/dist/components/Card/Card.contract.js.map +1 -0
- package/dist/components/Card/Card.d.ts +16 -0
- package/dist/components/Card/Card.d.ts.map +1 -0
- package/dist/components/Card/Card.js +11 -0
- package/dist/components/Card/Card.js.map +1 -0
- package/dist/components/Card/index.d.ts +3 -0
- package/dist/components/Card/index.d.ts.map +1 -0
- package/dist/components/Card/index.js +3 -0
- package/dist/components/Card/index.js.map +1 -0
- package/dist/components/CardGrid/CardGrid.contract.d.ts +3 -0
- package/dist/components/CardGrid/CardGrid.contract.d.ts.map +1 -0
- package/dist/components/CardGrid/CardGrid.contract.js +67 -0
- package/dist/components/CardGrid/CardGrid.contract.js.map +1 -0
- package/dist/components/CardGrid/CardGrid.d.ts +9 -0
- package/dist/components/CardGrid/CardGrid.d.ts.map +1 -0
- package/dist/components/CardGrid/CardGrid.js +7 -0
- package/dist/components/CardGrid/CardGrid.js.map +1 -0
- package/dist/components/CardGrid/index.d.ts +3 -0
- package/dist/components/CardGrid/index.d.ts.map +1 -0
- package/dist/components/CardGrid/index.js +3 -0
- package/dist/components/CardGrid/index.js.map +1 -0
- package/dist/components/Divider/Divider.contract.d.ts +3 -0
- package/dist/components/Divider/Divider.contract.d.ts.map +1 -0
- package/dist/components/Divider/Divider.contract.js +77 -0
- package/dist/components/Divider/Divider.contract.js.map +1 -0
- package/dist/components/Divider/Divider.d.ts +8 -0
- package/dist/components/Divider/Divider.d.ts.map +1 -0
- package/dist/components/Divider/Divider.js +10 -0
- package/dist/components/Divider/Divider.js.map +1 -0
- package/dist/components/Divider/index.d.ts +3 -0
- package/dist/components/Divider/index.d.ts.map +1 -0
- package/dist/components/Divider/index.js +3 -0
- package/dist/components/Divider/index.js.map +1 -0
- package/dist/components/FAQ/FAQ.contract.d.ts +3 -0
- package/dist/components/FAQ/FAQ.contract.d.ts.map +1 -0
- package/dist/components/FAQ/FAQ.contract.js +93 -0
- package/dist/components/FAQ/FAQ.contract.js.map +1 -0
- package/dist/components/FAQ/FAQ.d.ts +14 -0
- package/dist/components/FAQ/FAQ.d.ts.map +1 -0
- package/dist/components/FAQ/FAQ.js +7 -0
- package/dist/components/FAQ/FAQ.js.map +1 -0
- package/dist/components/FAQ/index.d.ts +3 -0
- package/dist/components/FAQ/index.d.ts.map +1 -0
- package/dist/components/FAQ/index.js +3 -0
- package/dist/components/FAQ/index.js.map +1 -0
- package/dist/components/Hero/Hero.contract.d.ts +3 -0
- package/dist/components/Hero/Hero.contract.d.ts.map +1 -0
- package/dist/components/Hero/Hero.contract.js +171 -0
- package/dist/components/Hero/Hero.contract.js.map +1 -0
- package/dist/components/Hero/Hero.d.ts +24 -0
- package/dist/components/Hero/Hero.d.ts.map +1 -0
- package/dist/components/Hero/Hero.js +7 -0
- package/dist/components/Hero/Hero.js.map +1 -0
- package/dist/components/Hero/index.d.ts +3 -0
- package/dist/components/Hero/index.d.ts.map +1 -0
- package/dist/components/Hero/index.js +3 -0
- package/dist/components/Hero/index.js.map +1 -0
- package/dist/components/MediaBlock/MediaBlock.contract.d.ts +3 -0
- package/dist/components/MediaBlock/MediaBlock.contract.d.ts.map +1 -0
- package/dist/components/MediaBlock/MediaBlock.contract.js +83 -0
- package/dist/components/MediaBlock/MediaBlock.contract.js.map +1 -0
- package/dist/components/MediaBlock/MediaBlock.d.ts +23 -0
- package/dist/components/MediaBlock/MediaBlock.d.ts.map +1 -0
- package/dist/components/MediaBlock/MediaBlock.js +8 -0
- package/dist/components/MediaBlock/MediaBlock.js.map +1 -0
- package/dist/components/MediaBlock/index.d.ts +3 -0
- package/dist/components/MediaBlock/index.d.ts.map +1 -0
- package/dist/components/MediaBlock/index.js +3 -0
- package/dist/components/MediaBlock/index.js.map +1 -0
- package/dist/components/Prose/Prose.contract.d.ts +3 -0
- package/dist/components/Prose/Prose.contract.d.ts.map +1 -0
- package/dist/components/Prose/Prose.contract.js +95 -0
- package/dist/components/Prose/Prose.contract.js.map +1 -0
- package/dist/components/Prose/Prose.d.ts +9 -0
- package/dist/components/Prose/Prose.d.ts.map +1 -0
- package/dist/components/Prose/Prose.js +7 -0
- package/dist/components/Prose/Prose.js.map +1 -0
- package/dist/components/Prose/index.d.ts +3 -0
- package/dist/components/Prose/index.d.ts.map +1 -0
- package/dist/components/Prose/index.js +3 -0
- package/dist/components/Prose/index.js.map +1 -0
- package/dist/components/Quote/Quote.contract.d.ts +3 -0
- package/dist/components/Quote/Quote.contract.d.ts.map +1 -0
- package/dist/components/Quote/Quote.contract.js +90 -0
- package/dist/components/Quote/Quote.contract.js.map +1 -0
- package/dist/components/Quote/Quote.d.ts +16 -0
- package/dist/components/Quote/Quote.d.ts.map +1 -0
- package/dist/components/Quote/Quote.js +7 -0
- package/dist/components/Quote/Quote.js.map +1 -0
- package/dist/components/Quote/index.d.ts +3 -0
- package/dist/components/Quote/index.d.ts.map +1 -0
- package/dist/components/Quote/index.js +3 -0
- package/dist/components/Quote/index.js.map +1 -0
- package/dist/components/SectionIntro/SectionIntro.contract.d.ts +3 -0
- package/dist/components/SectionIntro/SectionIntro.contract.d.ts.map +1 -0
- package/dist/components/SectionIntro/SectionIntro.contract.js +97 -0
- package/dist/components/SectionIntro/SectionIntro.contract.js.map +1 -0
- package/dist/components/SectionIntro/SectionIntro.d.ts +10 -0
- package/dist/components/SectionIntro/SectionIntro.d.ts.map +1 -0
- package/dist/components/SectionIntro/SectionIntro.js +7 -0
- package/dist/components/SectionIntro/SectionIntro.js.map +1 -0
- package/dist/components/SectionIntro/index.d.ts +3 -0
- package/dist/components/SectionIntro/index.d.ts.map +1 -0
- package/dist/components/SectionIntro/index.js +3 -0
- package/dist/components/SectionIntro/index.js.map +1 -0
- package/dist/components/Spacer/Spacer.contract.d.ts +3 -0
- package/dist/components/Spacer/Spacer.contract.d.ts.map +1 -0
- package/dist/components/Spacer/Spacer.contract.js +66 -0
- package/dist/components/Spacer/Spacer.contract.js.map +1 -0
- package/dist/components/Spacer/Spacer.d.ts +7 -0
- package/dist/components/Spacer/Spacer.d.ts.map +1 -0
- package/dist/components/Spacer/Spacer.js +7 -0
- package/dist/components/Spacer/Spacer.js.map +1 -0
- package/dist/components/Spacer/index.d.ts +3 -0
- package/dist/components/Spacer/index.d.ts.map +1 -0
- package/dist/components/Spacer/index.js +3 -0
- package/dist/components/Spacer/index.js.map +1 -0
- package/dist/components/Stats/Stats.contract.d.ts +3 -0
- package/dist/components/Stats/Stats.contract.d.ts.map +1 -0
- package/dist/components/Stats/Stats.contract.js +92 -0
- package/dist/components/Stats/Stats.contract.js.map +1 -0
- package/dist/components/Stats/Stats.d.ts +13 -0
- package/dist/components/Stats/Stats.d.ts.map +1 -0
- package/dist/components/Stats/Stats.js +7 -0
- package/dist/components/Stats/Stats.js.map +1 -0
- package/dist/components/Stats/index.d.ts +3 -0
- package/dist/components/Stats/index.d.ts.map +1 -0
- package/dist/components/Stats/index.js +3 -0
- package/dist/components/Stats/index.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.json +2014 -0
- package/dist/styles/base.css +55 -0
- package/dist/styles/index.css +213 -0
- package/dist/styles/reset.css +48 -0
- package/dist/styles/tokens.css +105 -0
- package/dist/tokens/colors.d.ts +48 -0
- package/dist/tokens/colors.d.ts.map +1 -0
- package/dist/tokens/colors.js +47 -0
- package/dist/tokens/colors.js.map +1 -0
- package/dist/tokens/index.d.ts +99 -0
- package/dist/tokens/index.d.ts.map +1 -0
- package/dist/tokens/index.js +21 -0
- package/dist/tokens/index.js.map +1 -0
- package/dist/tokens/radii.d.ts +11 -0
- package/dist/tokens/radii.d.ts.map +1 -0
- package/dist/tokens/radii.js +10 -0
- package/dist/tokens/radii.js.map +1 -0
- package/dist/tokens/spacing.d.ts +26 -0
- package/dist/tokens/spacing.d.ts.map +1 -0
- package/dist/tokens/spacing.js +25 -0
- package/dist/tokens/spacing.js.map +1 -0
- package/dist/tokens/typography.d.ts +28 -0
- package/dist/tokens/typography.d.ts.map +1 -0
- package/dist/tokens/typography.js +27 -0
- package/dist/tokens/typography.js.map +1 -0
- package/package.json +88 -7
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# @forumone/throughline-reference-ds
|
|
2
|
+
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#11](https://github.com/forumone/throughline/pull/11) [`b46635d`](https://github.com/forumone/throughline/commit/b46635d311d7172c0f6d439b9146f809f2ce2339) Thanks [@briangraves](https://github.com/briangraves)! - Initial release. Twelve components (Hero, SectionIntro, Prose, MediaBlock, Card, CardGrid, CTASection, Stats, FAQ, Quote, Divider, Spacer) with full `ComponentContract` metadata, Storybook stories, unit tests, and a generated manifest. Serves as the reference implementation for contract compliance, a test fixture for core packages, and a starting template for client projects without their own design system.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Forum One Communications Corporation
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,45 +1,119 @@
|
|
|
1
1
|
# @forumone/throughline-reference-ds
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A competent, brand-neutral reference design system that demonstrates contract compliance for Throughline. Twelve components, a CSS-variable token layer, a generated AI-ready manifest, and Storybook.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## What this package provides
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Twelve components covering the common editorial surface:
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
| Component | Purpose |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| `Hero` | Page opener with headline, body, CTAs, optional media — three variants |
|
|
12
|
+
| `SectionIntro` | h2-level section opener with optional eyebrow and body |
|
|
13
|
+
| `Prose` | Typographic container for long-form rich-text content |
|
|
14
|
+
| `MediaBlock` | Single image or video with optional caption and aspect ratios |
|
|
15
|
+
| `Card` | Linked or static content card with image, title, description, link |
|
|
16
|
+
| `CardGrid` | 2/3/4-column responsive layout for Cards |
|
|
17
|
+
| `CTASection` | Page-bottom call to action with one or two buttons |
|
|
18
|
+
| `Stats` | 2–4 headline metrics as a semantic `<dl>` |
|
|
19
|
+
| `FAQ` | Disclosure-style Q&A using native `<details>`/`<summary>` |
|
|
20
|
+
| `Quote` | Pullquote or testimonial with optional attribution |
|
|
21
|
+
| `Divider` | Decorative or semantic horizontal rule |
|
|
22
|
+
| `Spacer` | Explicit token-sized vertical spacing |
|
|
10
23
|
|
|
11
|
-
|
|
12
|
-
1. Configure OIDC trusted publishing for the package name `@forumone/throughline-reference-ds`
|
|
13
|
-
2. Enable secure, token-less publishing from CI/CD workflows
|
|
14
|
-
3. Establish provenance for packages published under this name
|
|
24
|
+
Every component ships a `ComponentContract` that conforms to `@forumone/throughline-design-contract@^0.2.0`. The package exposes the aggregated manifest at the `./manifest` subpath so any MCP server or client app can read it directly.
|
|
15
25
|
|
|
16
|
-
##
|
|
26
|
+
## Three ways to use this package
|
|
17
27
|
|
|
18
|
-
|
|
28
|
+
**As a starting point.** Copy components into your own design system. Customize freely; keep what works.
|
|
19
29
|
|
|
20
|
-
|
|
30
|
+
**As a test fixture.** Core framework packages (Component Server, Publishing Server) consume this DS to verify their behaviour against a realistic, contract-compliant design system.
|
|
21
31
|
|
|
22
|
-
|
|
32
|
+
**As a demo.** Stand up a Throughline site in minutes using only these twelve components — useful for prototypes, internal tools, and pitches.
|
|
23
33
|
|
|
24
|
-
|
|
25
|
-
2. Configure the trusted publisher (e.g., GitHub Actions)
|
|
26
|
-
3. Specify the repository and workflow that should be allowed to publish
|
|
27
|
-
4. Use the configured workflow to publish your actual package
|
|
34
|
+
## Installation
|
|
28
35
|
|
|
29
|
-
|
|
36
|
+
```bash
|
|
37
|
+
pnpm add @forumone/throughline-reference-ds
|
|
38
|
+
```
|
|
30
39
|
|
|
31
|
-
|
|
32
|
-
- Contains no executable code
|
|
33
|
-
- Provides no functionality
|
|
34
|
-
- Should not be installed as a dependency
|
|
35
|
-
- Exists only for administrative purposes
|
|
40
|
+
Peers: `react` + `react-dom` 18 or 19.
|
|
36
41
|
|
|
37
|
-
##
|
|
42
|
+
## Using components
|
|
38
43
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
44
|
+
```tsx
|
|
45
|
+
import '@forumone/throughline-reference-ds/styles.css'
|
|
46
|
+
import { Hero, CardGrid, Card } from '@forumone/throughline-reference-ds'
|
|
42
47
|
|
|
43
|
-
|
|
48
|
+
export function HomePage() {
|
|
49
|
+
return (
|
|
50
|
+
<>
|
|
51
|
+
<Hero
|
|
52
|
+
eyebrow="New program"
|
|
53
|
+
headline="Fellowship for climate researchers"
|
|
54
|
+
body="A one-year fellowship supporting researchers at the intersection of climate and community resilience."
|
|
55
|
+
cta={{ label: 'Apply now', url: '/apply' }}
|
|
56
|
+
/>
|
|
57
|
+
<CardGrid columns={3}>
|
|
58
|
+
<Card title="Research" description="…" link={{ label: 'Read', url: '#' }} />
|
|
59
|
+
<Card title="Partnerships" description="…" link={{ label: 'Read', url: '#' }} />
|
|
60
|
+
<Card title="Funding" description="…" link={{ label: 'Read', url: '#' }} />
|
|
61
|
+
</CardGrid>
|
|
62
|
+
</>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
```
|
|
44
66
|
|
|
45
|
-
|
|
67
|
+
## Reading the manifest
|
|
68
|
+
|
|
69
|
+
The aggregated manifest ships at the `./manifest` subpath. Component servers, lint tooling, and documentation sites consume it:
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
import manifest from '@forumone/throughline-reference-ds/manifest'
|
|
73
|
+
import { loadManifest } from '@forumone/throughline-design-contract'
|
|
74
|
+
|
|
75
|
+
const loaded = loadManifest(manifest)
|
|
76
|
+
console.log(loaded.listComponents())
|
|
77
|
+
// ['Card', 'CardGrid', 'CTASection', 'Divider', 'FAQ', 'Hero', 'MediaBlock', 'Prose', 'Quote', 'SectionIntro', 'Spacer', 'Stats']
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Theming via CSS variables
|
|
81
|
+
|
|
82
|
+
Every token is exposed as a CSS variable on `:root`. Override them to rebrand without touching component code:
|
|
83
|
+
|
|
84
|
+
```css
|
|
85
|
+
:root {
|
|
86
|
+
--color-brand-primary: #7E33FF;
|
|
87
|
+
--color-brand-primary-hover: #6420CC;
|
|
88
|
+
--font-family-sans: 'DM Sans', system-ui, sans-serif;
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The package also ships a `prefers-color-scheme: dark` override that swaps background, text, and border tokens. Clients with more elaborate dark-mode requirements should override the entire token layer.
|
|
93
|
+
|
|
94
|
+
Full token list: `src/styles/tokens.css` (generated from `src/tokens/*.ts` via `pnpm build:tokens-css`).
|
|
95
|
+
|
|
96
|
+
## Storybook
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
pnpm --filter @forumone/throughline-reference-ds storybook # dev server on :6006
|
|
100
|
+
pnpm --filter @forumone/throughline-reference-ds build-storybook # produces storybook-static/
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The static build is used by `pnpm validate` to verify that every `storyId` declared on a contract resolves to a real Storybook story.
|
|
104
|
+
|
|
105
|
+
## CI validation
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
pnpm --filter @forumone/throughline-reference-ds build
|
|
109
|
+
pnpm --filter @forumone/throughline-reference-ds build-storybook
|
|
110
|
+
pnpm --filter @forumone/throughline-reference-ds validate
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`validate` runs `lintManifest` from `@forumone/throughline-design-contract/lint` against the generated manifest. Errors fail the build; warnings print but pass.
|
|
114
|
+
|
|
115
|
+
## Authoring your own design system
|
|
116
|
+
|
|
117
|
+
Use this package as a reference. Each component has a `.contract.ts` file; every contract satisfies `ComponentContractSchema`. Copy the structure, replace the content, and your design system becomes a valid input to Throughline.
|
|
118
|
+
|
|
119
|
+
The authoring guide lives at `docs/building-plugins.md` (for MCP plugins) and the contract authoring expectations live in `@forumone/throughline-design-contract`'s README.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CTASection.contract.d.ts","sourceRoot":"","sources":["../../../src/components/CTASection/CTASection.contract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAA;AAE9E,eAAO,MAAM,QAAQ,EAAE,iBA6HtB,CAAA"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
export const contract = {
|
|
2
|
+
name: 'CTASection',
|
|
3
|
+
category: 'cta',
|
|
4
|
+
description: 'A page-bottom call to action with a headline, optional supporting copy, and one or two buttons.',
|
|
5
|
+
intent: 'Close a page with a clear next step. Use at the end of program pages, campaigns, and marketing pages where the reader has enough context to act. Do not use at the top of a page (that is Hero\'s job) or as a section opener (that is SectionIntro\'s).',
|
|
6
|
+
composition: {
|
|
7
|
+
placement: ['page', 'section'],
|
|
8
|
+
maxPerPage: 2,
|
|
9
|
+
requiredSiblings: [],
|
|
10
|
+
forbiddenAdjacent: ['CTASection'],
|
|
11
|
+
},
|
|
12
|
+
content: {
|
|
13
|
+
fields: [
|
|
14
|
+
{
|
|
15
|
+
name: 'headline',
|
|
16
|
+
type: 'text',
|
|
17
|
+
required: true,
|
|
18
|
+
maxLength: 100,
|
|
19
|
+
constraints: 'Direct, imperative; reads like an invitation',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: 'body',
|
|
23
|
+
type: 'text',
|
|
24
|
+
required: false,
|
|
25
|
+
maxLength: 200,
|
|
26
|
+
constraints: 'One sentence that reduces friction to clicking',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: 'cta',
|
|
30
|
+
type: 'group',
|
|
31
|
+
required: true,
|
|
32
|
+
of: [
|
|
33
|
+
{ name: 'label', type: 'text', required: true, maxLength: 30 },
|
|
34
|
+
{ name: 'url', type: 'link', required: true },
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: 'secondaryCta',
|
|
39
|
+
type: 'group',
|
|
40
|
+
required: false,
|
|
41
|
+
of: [
|
|
42
|
+
{ name: 'label', type: 'text', required: true, maxLength: 30 },
|
|
43
|
+
{ name: 'url', type: 'link', required: true },
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
variants: [
|
|
48
|
+
{ name: 'primary', description: 'Standard background', whenToUse: 'Default' },
|
|
49
|
+
{ name: 'secondary', description: 'Subtle grey background', whenToUse: 'Most page closes — less visual weight than Hero' },
|
|
50
|
+
{ name: 'inverse', description: 'Dark background', whenToUse: 'Campaign pages or high-stakes calls to action' },
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
tokens: {
|
|
54
|
+
consumes: [
|
|
55
|
+
'color.bg.primary',
|
|
56
|
+
'color.bg.secondary',
|
|
57
|
+
'color.bg.inverse',
|
|
58
|
+
'color.text.primary',
|
|
59
|
+
'color.text.secondary',
|
|
60
|
+
'color.text.inverse',
|
|
61
|
+
'color.brand.primary',
|
|
62
|
+
'color.brand.primaryHover',
|
|
63
|
+
'color.border.strong',
|
|
64
|
+
'spacing.section',
|
|
65
|
+
'spacing.container',
|
|
66
|
+
'spacing.8',
|
|
67
|
+
'spacing.6',
|
|
68
|
+
'spacing.4',
|
|
69
|
+
'spacing.3',
|
|
70
|
+
'font.size.4xl',
|
|
71
|
+
'font.size.lg',
|
|
72
|
+
'font.weight.bold',
|
|
73
|
+
'font.weight.semibold',
|
|
74
|
+
'line.height.tight',
|
|
75
|
+
'line.height.relaxed',
|
|
76
|
+
'radius.md',
|
|
77
|
+
],
|
|
78
|
+
configurable: [
|
|
79
|
+
{
|
|
80
|
+
prop: 'background',
|
|
81
|
+
tokenGroup: 'color.bg',
|
|
82
|
+
allowedValues: ['primary', 'secondary', 'inverse'],
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
},
|
|
86
|
+
accessibility: {
|
|
87
|
+
keyboardSupport: ['Tab to primary CTA', 'Tab to secondary CTA'],
|
|
88
|
+
screenReaderBehavior: 'The headline is announced as an h2. CTAs are standard links; their label text is announced as the link name.',
|
|
89
|
+
contentWarnings: [
|
|
90
|
+
'Avoid verb-only labels ("Click", "Go") — include the destination ("Start building", "Read the docs")',
|
|
91
|
+
],
|
|
92
|
+
},
|
|
93
|
+
examples: [
|
|
94
|
+
{ label: 'Default', intent: 'Two-button page close', storyId: 'cta-section--default' },
|
|
95
|
+
{ label: 'Primary only', intent: 'Single-action CTA', storyId: 'cta-section--primary-only' },
|
|
96
|
+
{ label: 'Inverse', intent: 'High-contrast campaign CTA', storyId: 'cta-section--inverse' },
|
|
97
|
+
],
|
|
98
|
+
antiExamples: [
|
|
99
|
+
{
|
|
100
|
+
label: 'Three or more CTAs',
|
|
101
|
+
why: 'Too many options reduces conversion; pick the top one, put the rest on a detail page',
|
|
102
|
+
useInstead: 'One primary + optional secondary',
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
label: 'Using CTASection as a page opener',
|
|
106
|
+
why: 'Readers cannot convert without context; CTA belongs at the end',
|
|
107
|
+
useInstead: 'Hero',
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
behavior: {
|
|
111
|
+
fetchesData: false,
|
|
112
|
+
hasClientState: false,
|
|
113
|
+
animates: false,
|
|
114
|
+
requiresAnalytics: true,
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
//# sourceMappingURL=CTASection.contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CTASection.contract.js","sourceRoot":"","sources":["../../../src/components/CTASection/CTASection.contract.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type CTASectionBackground = 'primary' | 'secondary' | 'inverse';
|
|
2
|
+
export interface CTASectionProps {
|
|
3
|
+
headline: string;
|
|
4
|
+
body?: string;
|
|
5
|
+
cta: {
|
|
6
|
+
label: string;
|
|
7
|
+
url: string;
|
|
8
|
+
};
|
|
9
|
+
secondaryCta?: {
|
|
10
|
+
label: string;
|
|
11
|
+
url: string;
|
|
12
|
+
};
|
|
13
|
+
background?: CTASectionBackground;
|
|
14
|
+
className?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function CTASection({ headline, body, cta, secondaryCta, background, className, }: CTASectionProps): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
//# sourceMappingURL=CTASection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CTASection.d.ts","sourceRoot":"","sources":["../../../src/components/CTASection/CTASection.tsx"],"names":[],"mappings":"AAGA,MAAM,MAAM,oBAAoB,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,CAAA;AAEtE,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,GAAG,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;IACnC,YAAY,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;IAC7C,UAAU,CAAC,EAAE,oBAAoB,CAAA;IACjC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,IAAI,EACJ,GAAG,EACH,YAAY,EACZ,UAAwB,EACxB,SAAS,GACV,EAAE,eAAe,2CAmBjB"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { clsx } from 'clsx';
|
|
3
|
+
import styles from './CTASection.module.css';
|
|
4
|
+
export function CTASection({ headline, body, cta, secondaryCta, background = 'secondary', className, }) {
|
|
5
|
+
return (_jsx("section", { className: clsx(styles.cta, styles[`bg-${background}`], className), children: _jsxs("div", { className: styles.container, children: [_jsx("h2", { className: styles.headline, children: headline }), body ? _jsx("p", { className: styles.body, children: body }) : null, _jsxs("div", { className: styles.actions, children: [_jsx("a", { href: cta.url, className: styles.primary, children: cta.label }), secondaryCta ? (_jsx("a", { href: secondaryCta.url, className: styles.secondary, children: secondaryCta.label })) : null] })] }) }));
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=CTASection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CTASection.js","sourceRoot":"","sources":["../../../src/components/CTASection/CTASection.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,MAAM,MAAM,yBAAyB,CAAA;AAa5C,MAAM,UAAU,UAAU,CAAC,EACzB,QAAQ,EACR,IAAI,EACJ,GAAG,EACH,YAAY,EACZ,UAAU,GAAG,WAAW,EACxB,SAAS,GACO;IAChB,OAAO,CACL,kBAAS,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,UAAU,EAAE,CAAC,EAAE,SAAS,CAAC,YACzE,eAAK,SAAS,EAAE,MAAM,CAAC,SAAS,aAC9B,aAAI,SAAS,EAAE,MAAM,CAAC,QAAQ,YAAG,QAAQ,GAAM,EAC9C,IAAI,CAAC,CAAC,CAAC,YAAG,SAAS,EAAE,MAAM,CAAC,IAAI,YAAG,IAAI,GAAK,CAAC,CAAC,CAAC,IAAI,EACpD,eAAK,SAAS,EAAE,MAAM,CAAC,OAAO,aAC5B,YAAG,IAAI,EAAE,GAAG,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,CAAC,OAAO,YACxC,GAAG,CAAC,KAAK,GACR,EACH,YAAY,CAAC,CAAC,CAAC,CACd,YAAG,IAAI,EAAE,YAAY,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,YACnD,YAAY,CAAC,KAAK,GACjB,CACL,CAAC,CAAC,CAAC,IAAI,IACJ,IACF,GACE,CACX,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/CTASection/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAC7F,OAAO,EAAE,QAAQ,IAAI,kBAAkB,EAAE,MAAM,0BAA0B,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/CTASection/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAmD,MAAM,iBAAiB,CAAA;AAC7F,OAAO,EAAE,QAAQ,IAAI,kBAAkB,EAAE,MAAM,0BAA0B,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Card.contract.d.ts","sourceRoot":"","sources":["../../../src/components/Card/Card.contract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAA;AAE9E,eAAO,MAAM,QAAQ,EAAE,iBAuHtB,CAAA"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
export const contract = {
|
|
2
|
+
name: 'Card',
|
|
3
|
+
category: 'card',
|
|
4
|
+
description: 'A single content card with optional image, eyebrow, title, description, and link. Renders as a link when a link target is provided.',
|
|
5
|
+
intent: 'Surface a single piece of content in a scannable, consistent shape — a program page, a blog post, a case study, a staff bio. Cards are typically laid out in a CardGrid, but a single Card can appear alone inside a section when the content warrants singular emphasis.',
|
|
6
|
+
composition: {
|
|
7
|
+
placement: ['section', 'inline'],
|
|
8
|
+
maxPerPage: null,
|
|
9
|
+
requiredSiblings: [],
|
|
10
|
+
forbiddenAdjacent: [],
|
|
11
|
+
},
|
|
12
|
+
content: {
|
|
13
|
+
fields: [
|
|
14
|
+
{
|
|
15
|
+
name: 'eyebrow',
|
|
16
|
+
type: 'text',
|
|
17
|
+
required: false,
|
|
18
|
+
maxLength: 30,
|
|
19
|
+
constraints: 'Short label such as the content type ("Case study", "Program update")',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: 'image',
|
|
23
|
+
type: 'image',
|
|
24
|
+
required: false,
|
|
25
|
+
constraints: 'Landscape orientation; framing works best for images that read at small sizes',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: 'title',
|
|
29
|
+
type: 'text',
|
|
30
|
+
required: true,
|
|
31
|
+
maxLength: 100,
|
|
32
|
+
constraints: 'Sentence case; reads like a headline',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'description',
|
|
36
|
+
type: 'text',
|
|
37
|
+
required: false,
|
|
38
|
+
maxLength: 200,
|
|
39
|
+
constraints: 'One to two sentences that give the reader a reason to click',
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: 'link',
|
|
43
|
+
type: 'group',
|
|
44
|
+
required: false,
|
|
45
|
+
of: [
|
|
46
|
+
{ name: 'label', type: 'text', required: true, maxLength: 30 },
|
|
47
|
+
{ name: 'url', type: 'link', required: true },
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
variants: [
|
|
52
|
+
{ name: 'linked', description: 'Entire card is clickable', whenToUse: 'Teaser for a single piece of content' },
|
|
53
|
+
{ name: 'static', description: 'No link; presentational only', whenToUse: 'Inline informational cards (team bios, program descriptions) that do not link elsewhere' },
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
tokens: {
|
|
57
|
+
consumes: [
|
|
58
|
+
'color.bg.primary',
|
|
59
|
+
'color.bg.tertiary',
|
|
60
|
+
'color.text.primary',
|
|
61
|
+
'color.text.secondary',
|
|
62
|
+
'color.border.default',
|
|
63
|
+
'color.border.strong',
|
|
64
|
+
'color.brand.primary',
|
|
65
|
+
'spacing.6',
|
|
66
|
+
'spacing.2',
|
|
67
|
+
'font.size.xl',
|
|
68
|
+
'font.size.base',
|
|
69
|
+
'font.size.sm',
|
|
70
|
+
'font.size.xs',
|
|
71
|
+
'font.weight.semibold',
|
|
72
|
+
'line.height.snug',
|
|
73
|
+
'line.height.normal',
|
|
74
|
+
'letter.spacing.wide',
|
|
75
|
+
'radius.lg',
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
accessibility: {
|
|
79
|
+
keyboardSupport: ['When linked, the whole card is a single tab stop'],
|
|
80
|
+
screenReaderBehavior: 'Linked cards render as a single <a> element announcing the title + description + link label as one unit. Static cards render as <article> with an h3 title.',
|
|
81
|
+
contentWarnings: [
|
|
82
|
+
'Avoid nested interactive elements inside a linked card (buttons, additional links)',
|
|
83
|
+
'Always author alt text for card images or omit the image entirely',
|
|
84
|
+
],
|
|
85
|
+
},
|
|
86
|
+
examples: [
|
|
87
|
+
{ label: 'Default', intent: 'Content teaser with image and link', storyId: 'card--default' },
|
|
88
|
+
{ label: 'With eyebrow', intent: 'Labeled teaser highlighting content type', storyId: 'card--with-eyebrow' },
|
|
89
|
+
{ label: 'No image', intent: 'Text-only teaser for resources or downloads', storyId: 'card--no-image' },
|
|
90
|
+
{ label: 'Static', intent: 'Presentational card without a link target', storyId: 'card--static' },
|
|
91
|
+
],
|
|
92
|
+
antiExamples: [
|
|
93
|
+
{
|
|
94
|
+
label: 'Nesting interactive controls inside a linked card',
|
|
95
|
+
why: 'A linked card is a single tab stop; adding buttons or additional links creates nested interactive regions that confuse assistive technology',
|
|
96
|
+
useInstead: 'Use a static card with explicit nested controls, or move secondary actions to a detail page',
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
label: 'Using Card as a navigation menu item',
|
|
100
|
+
why: 'Cards are for content teasers; site navigation belongs in a Menu or Nav component',
|
|
101
|
+
useInstead: 'A dedicated navigation component',
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
behavior: {
|
|
105
|
+
fetchesData: false,
|
|
106
|
+
hasClientState: false,
|
|
107
|
+
animates: false,
|
|
108
|
+
requiresAnalytics: false,
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
//# sourceMappingURL=Card.contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Card.contract.js","sourceRoot":"","sources":["../../../src/components/Card/Card.contract.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface CardProps {
|
|
2
|
+
title: string;
|
|
3
|
+
description?: string;
|
|
4
|
+
image?: {
|
|
5
|
+
url: string;
|
|
6
|
+
alt: string;
|
|
7
|
+
};
|
|
8
|
+
link?: {
|
|
9
|
+
label: string;
|
|
10
|
+
url: string;
|
|
11
|
+
};
|
|
12
|
+
eyebrow?: string;
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function Card({ title, description, image, link, eyebrow, className }: CardProps): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
//# sourceMappingURL=Card.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Card.d.ts","sourceRoot":"","sources":["../../../src/components/Card/Card.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,KAAK,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;IACpC,IAAI,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;IACrC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,IAAI,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,SAAS,2CA0BtF"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { clsx } from 'clsx';
|
|
3
|
+
import styles from './Card.module.css';
|
|
4
|
+
export function Card({ title, description, image, link, eyebrow, className }) {
|
|
5
|
+
const body = (_jsxs(_Fragment, { children: [image ? (_jsx("div", { className: styles.imageFrame, children: _jsx("img", { src: image.url, alt: image.alt, className: styles.image }) })) : null, _jsxs("div", { className: styles.content, children: [eyebrow ? _jsx("p", { className: styles.eyebrow, children: eyebrow }) : null, _jsx("h3", { className: styles.title, children: title }), description ? _jsx("p", { className: styles.description, children: description }) : null, link ? _jsx("span", { className: styles.linkLabel, children: link.label }) : null] })] }));
|
|
6
|
+
if (link) {
|
|
7
|
+
return (_jsx("a", { href: link.url, className: clsx(styles.card, styles.linked, className), children: body }));
|
|
8
|
+
}
|
|
9
|
+
return _jsx("article", { className: clsx(styles.card, className), children: body });
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=Card.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Card.js","sourceRoot":"","sources":["../../../src/components/Card/Card.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,MAAM,MAAM,mBAAmB,CAAA;AAWtC,MAAM,UAAU,IAAI,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAa;IACrF,MAAM,IAAI,GAAG,CACX,8BACG,KAAK,CAAC,CAAC,CAAC,CACP,cAAK,SAAS,EAAE,MAAM,CAAC,UAAU,YAC/B,cAAK,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,CAAC,KAAK,GAAI,GAC5D,CACP,CAAC,CAAC,CAAC,IAAI,EACR,eAAK,SAAS,EAAE,MAAM,CAAC,OAAO,aAC3B,OAAO,CAAC,CAAC,CAAC,YAAG,SAAS,EAAE,MAAM,CAAC,OAAO,YAAG,OAAO,GAAK,CAAC,CAAC,CAAC,IAAI,EAC7D,aAAI,SAAS,EAAE,MAAM,CAAC,KAAK,YAAG,KAAK,GAAM,EACxC,WAAW,CAAC,CAAC,CAAC,YAAG,SAAS,EAAE,MAAM,CAAC,WAAW,YAAG,WAAW,GAAK,CAAC,CAAC,CAAC,IAAI,EACxE,IAAI,CAAC,CAAC,CAAC,eAAM,SAAS,EAAE,MAAM,CAAC,SAAS,YAAG,IAAI,CAAC,KAAK,GAAQ,CAAC,CAAC,CAAC,IAAI,IACjE,IACL,CACJ,CAAA;IAED,IAAI,IAAI,EAAE,CAAC;QACT,OAAO,CACL,YAAG,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,YACtE,IAAI,GACH,CACL,CAAA;IACH,CAAC;IAED,OAAO,kBAAS,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,YAAG,IAAI,GAAW,CAAA;AAC3E,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Card/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,KAAK,SAAS,EAAE,MAAM,WAAW,CAAA;AAChD,OAAO,EAAE,QAAQ,IAAI,YAAY,EAAE,MAAM,oBAAoB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/Card/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAkB,MAAM,WAAW,CAAA;AAChD,OAAO,EAAE,QAAQ,IAAI,YAAY,EAAE,MAAM,oBAAoB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CardGrid.contract.d.ts","sourceRoot":"","sources":["../../../src/components/CardGrid/CardGrid.contract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAA;AAE9E,eAAO,MAAM,QAAQ,EAAE,iBA2EtB,CAAA"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
export const contract = {
|
|
2
|
+
name: 'CardGrid',
|
|
3
|
+
category: 'section',
|
|
4
|
+
description: 'A responsive layout container for two, three, or four Cards per row. Reflows to fewer columns on narrow viewports.',
|
|
5
|
+
intent: 'Lay out multiple Card components inside a section. Use for program lists, blog indexes, staff directories, and any collection of similarly-shaped content. CardGrid is purely layout; it does not fetch data or filter — authors list the Cards they want.',
|
|
6
|
+
composition: {
|
|
7
|
+
placement: ['section'],
|
|
8
|
+
maxPerPage: null,
|
|
9
|
+
requiredSiblings: ['Card'],
|
|
10
|
+
forbiddenAdjacent: [],
|
|
11
|
+
allowedSlots: { children: ['Card'] },
|
|
12
|
+
},
|
|
13
|
+
content: {
|
|
14
|
+
fields: [
|
|
15
|
+
{
|
|
16
|
+
name: 'children',
|
|
17
|
+
type: 'array',
|
|
18
|
+
required: true,
|
|
19
|
+
constraints: 'An array of Card components',
|
|
20
|
+
of: [{ name: 'card', type: 'group', required: true }],
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
variants: [
|
|
24
|
+
{ name: '2', description: 'Two columns', whenToUse: 'Lists of 2-4 prominent items' },
|
|
25
|
+
{ name: '3', description: 'Three columns', whenToUse: 'Default — works for most collections' },
|
|
26
|
+
{ name: '4', description: 'Four columns', whenToUse: 'Denser lists of 8 or more short cards' },
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
tokens: {
|
|
30
|
+
consumes: ['spacing.container', 'spacing.6'],
|
|
31
|
+
configurable: [
|
|
32
|
+
{ prop: 'columns', tokenGroup: 'layout', allowedValues: ['2', '3', '4'] },
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
accessibility: {
|
|
36
|
+
keyboardSupport: [],
|
|
37
|
+
screenReaderBehavior: 'Purely visual layout. Screen readers traverse children in source order; CardGrid itself adds no ARIA.',
|
|
38
|
+
contentWarnings: [
|
|
39
|
+
'Children must be Cards; mixing other components breaks the visual rhythm',
|
|
40
|
+
'Keep all cards roughly the same content length — dramatically unequal cards look broken',
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
examples: [
|
|
44
|
+
{ label: 'Two columns', intent: 'Featured program pair', storyId: 'card-grid--two-column' },
|
|
45
|
+
{ label: 'Three columns', intent: 'Standard content index', storyId: 'card-grid--three-column' },
|
|
46
|
+
{ label: 'Four columns', intent: 'Dense staff directory', storyId: 'card-grid--four-column' },
|
|
47
|
+
],
|
|
48
|
+
antiExamples: [
|
|
49
|
+
{
|
|
50
|
+
label: 'Mixing Cards and non-Card children',
|
|
51
|
+
why: 'The grid assumes uniform children; mixing Stats or MediaBlocks produces broken columns',
|
|
52
|
+
useInstead: 'Group by type — one CardGrid, then the other components below',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
label: 'Using CardGrid with a single Card',
|
|
56
|
+
why: 'A CardGrid wrapping one Card is overhead without benefit',
|
|
57
|
+
useInstead: 'Render the Card directly',
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
behavior: {
|
|
61
|
+
fetchesData: false,
|
|
62
|
+
hasClientState: false,
|
|
63
|
+
animates: false,
|
|
64
|
+
requiresAnalytics: false,
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
//# sourceMappingURL=CardGrid.contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CardGrid.contract.js","sourceRoot":"","sources":["../../../src/components/CardGrid/CardGrid.contract.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,QAAQ,GAAsB;IACzC,IAAI,EAAE,UAAU;IAChB,QAAQ,EAAE,SAAS;IACnB,WAAW,EACT,oHAAoH;IACtH,MAAM,EACJ,4PAA4P;IAE9P,WAAW,EAAE;QACX,SAAS,EAAE,CAAC,SAAS,CAAC;QACtB,UAAU,EAAE,IAAI;QAChB,gBAAgB,EAAE,CAAC,MAAM,CAAC;QAC1B,iBAAiB,EAAE,EAAE;QACrB,YAAY,EAAE,EAAE,QAAQ,EAAE,CAAC,MAAM,CAAC,EAAE;KACrC;IAED,OAAO,EAAE;QACP,MAAM,EAAE;YACN;gBACE,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,OAAO;gBACb,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE,6BAA6B;gBAC1C,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;aACtD;SACF;QACD,QAAQ,EAAE;YACR,EAAE,IAAI,EAAE,GAAG,EAAE,WAAW,EAAE,aAAa,EAAE,SAAS,EAAE,8BAA8B,EAAE;YACpF,EAAE,IAAI,EAAE,GAAG,EAAE,WAAW,EAAE,eAAe,EAAE,SAAS,EAAE,sCAAsC,EAAE;YAC9F,EAAE,IAAI,EAAE,GAAG,EAAE,WAAW,EAAE,cAAc,EAAE,SAAS,EAAE,uCAAuC,EAAE;SAC/F;KACF;IAED,MAAM,EAAE;QACN,QAAQ,EAAE,CAAC,mBAAmB,EAAE,WAAW,CAAC;QAC5C,YAAY,EAAE;YACZ,EAAE,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,aAAa,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE;SAC1E;KACF;IAED,aAAa,EAAE;QACb,eAAe,EAAE,EAAE;QACnB,oBAAoB,EAClB,uGAAuG;QACzG,eAAe,EAAE;YACf,0EAA0E;YAC1E,yFAAyF;SAC1F;KACF;IAED,QAAQ,EAAE;QACR,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,uBAAuB,EAAE,OAAO,EAAE,uBAAuB,EAAE;QAC3F,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,wBAAwB,EAAE,OAAO,EAAE,yBAAyB,EAAE;QAChG,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,uBAAuB,EAAE,OAAO,EAAE,wBAAwB,EAAE;KAC9F;IAED,YAAY,EAAE;QACZ;YACE,KAAK,EAAE,oCAAoC;YAC3C,GAAG,EAAE,wFAAwF;YAC7F,UAAU,EAAE,+DAA+D;SAC5E;QACD;YACE,KAAK,EAAE,mCAAmC;YAC1C,GAAG,EAAE,0DAA0D;YAC/D,UAAU,EAAE,0BAA0B;SACvC;KACF;IAED,QAAQ,EAAE;QACR,WAAW,EAAE,KAAK;QAClB,cAAc,EAAE,KAAK;QACrB,QAAQ,EAAE,KAAK;QACf,iBAAiB,EAAE,KAAK;KACzB;CACF,CAAA"}
|