@forumone/create-throughline 0.2.1 → 0.3.1
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 +21 -0
- package/dist/generator.d.ts +2 -0
- package/dist/generator.d.ts.map +1 -1
- package/dist/generator.js +23 -4
- package/dist/generator.js.map +1 -1
- package/dist/post-install.js +1 -1
- package/dist/post-install.js.map +1 -1
- package/dist/templates/base/README.md +8 -9
- package/dist/templates/base/apps/web/package.json.template +11 -10
- package/dist/templates/base/apps/web/src/app/api/inngest/route.ts.template +33 -4
- package/dist/templates/base/apps/web/src/payload.config.ts.template +21 -6
- package/dist/templates/base/apps/web/tsconfig.json +2 -1
- package/dist/templates/base/gitignore +15 -0
- package/dist/templates/base/package.json.template +4 -1
- package/dist/templates/base/pnpm-workspace.yaml +1 -0
- package/dist/templates/base/turbo.json +11 -0
- package/dist/templates/with-reference-ds/design-system/.storybook/main.ts +21 -0
- package/dist/templates/with-reference-ds/design-system/.storybook/preview.ts +19 -0
- package/dist/templates/with-reference-ds/design-system/README.md +33 -0
- package/dist/templates/with-reference-ds/design-system/gitignore +5 -0
- package/dist/templates/with-reference-ds/design-system/package.json.template +62 -0
- package/dist/templates/with-reference-ds/design-system/scripts/build-manifest.ts +89 -0
- package/dist/templates/with-reference-ds/design-system/scripts/build-styles.ts +28 -0
- package/dist/templates/with-reference-ds/design-system/scripts/build-tokens-css.ts +43 -0
- package/dist/templates/with-reference-ds/design-system/scripts/validate.ts +58 -0
- package/dist/templates/with-reference-ds/design-system/src/components/CTASection/CTASection.contract.ts +128 -0
- package/dist/templates/with-reference-ds/design-system/src/components/CTASection/CTASection.module.css +83 -0
- package/dist/templates/with-reference-ds/design-system/src/components/CTASection/CTASection.stories.tsx +39 -0
- package/dist/templates/with-reference-ds/design-system/src/components/CTASection/CTASection.test.tsx +27 -0
- package/dist/templates/with-reference-ds/design-system/src/components/CTASection/CTASection.tsx +41 -0
- package/dist/templates/with-reference-ds/design-system/src/components/CTASection/index.ts +2 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Card/Card.contract.ts +122 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Card/Card.module.css +64 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Card/Card.stories.tsx +45 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Card/Card.test.tsx +37 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Card/Card.tsx +39 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Card/index.ts +2 -0
- package/dist/templates/with-reference-ds/design-system/src/components/CardGrid/CardGrid.contract.ts +78 -0
- package/dist/templates/with-reference-ds/design-system/src/components/CardGrid/CardGrid.module.css +34 -0
- package/dist/templates/with-reference-ds/design-system/src/components/CardGrid/CardGrid.stories.tsx +36 -0
- package/dist/templates/with-reference-ds/design-system/src/components/CardGrid/CardGrid.test.tsx +25 -0
- package/dist/templates/with-reference-ds/design-system/src/components/CardGrid/CardGrid.tsx +19 -0
- package/dist/templates/with-reference-ds/design-system/src/components/CardGrid/index.ts +2 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Divider/Divider.contract.ts +87 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Divider/Divider.module.css +18 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Divider/Divider.stories.tsx +35 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Divider/Divider.test.tsx +20 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Divider/Divider.tsx +17 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Divider/index.ts +2 -0
- package/dist/templates/with-reference-ds/design-system/src/components/FAQ/FAQ.contract.ts +104 -0
- package/dist/templates/with-reference-ds/design-system/src/components/FAQ/FAQ.module.css +66 -0
- package/dist/templates/with-reference-ds/design-system/src/components/FAQ/FAQ.stories.tsx +61 -0
- package/dist/templates/with-reference-ds/design-system/src/components/FAQ/FAQ.test.tsx +30 -0
- package/dist/templates/with-reference-ds/design-system/src/components/FAQ/FAQ.tsx +41 -0
- package/dist/templates/with-reference-ds/design-system/src/components/FAQ/index.ts +2 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Hero/Hero.contract.ts +182 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Hero/Hero.module.css +124 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Hero/Hero.stories.tsx +58 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Hero/Hero.test.tsx +49 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Hero/Hero.tsx +67 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Hero/index.ts +2 -0
- package/dist/templates/with-reference-ds/design-system/src/components/MediaBlock/MediaBlock.contract.ts +94 -0
- package/dist/templates/with-reference-ds/design-system/src/components/MediaBlock/MediaBlock.module.css +49 -0
- package/dist/templates/with-reference-ds/design-system/src/components/MediaBlock/MediaBlock.stories.tsx +37 -0
- package/dist/templates/with-reference-ds/design-system/src/components/MediaBlock/MediaBlock.test.tsx +35 -0
- package/dist/templates/with-reference-ds/design-system/src/components/MediaBlock/MediaBlock.tsx +49 -0
- package/dist/templates/with-reference-ds/design-system/src/components/MediaBlock/index.ts +7 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Prose/Prose.contract.ts +107 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Prose/Prose.module.css +72 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Prose/Prose.stories.tsx +39 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Prose/Prose.test.tsx +26 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Prose/Prose.tsx +15 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Prose/index.ts +2 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Quote/Quote.contract.ts +101 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Quote/Quote.module.css +59 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Quote/Quote.stories.tsx +44 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Quote/Quote.test.tsx +37 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Quote/Quote.tsx +40 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Quote/index.ts +2 -0
- package/dist/templates/with-reference-ds/design-system/src/components/SectionIntro/SectionIntro.contract.ts +108 -0
- package/dist/templates/with-reference-ds/design-system/src/components/SectionIntro/SectionIntro.module.css +36 -0
- package/dist/templates/with-reference-ds/design-system/src/components/SectionIntro/SectionIntro.stories.tsx +33 -0
- package/dist/templates/with-reference-ds/design-system/src/components/SectionIntro/SectionIntro.test.tsx +28 -0
- package/dist/templates/with-reference-ds/design-system/src/components/SectionIntro/SectionIntro.tsx +28 -0
- package/dist/templates/with-reference-ds/design-system/src/components/SectionIntro/index.ts +2 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Spacer/Spacer.contract.ts +76 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Spacer/Spacer.module.css +23 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Spacer/Spacer.stories.tsx +25 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Spacer/Spacer.test.tsx +17 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Spacer/Spacer.tsx +19 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Spacer/index.ts +2 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Stats/Stats.contract.ts +103 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Stats/Stats.module.css +88 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Stats/Stats.stories.tsx +44 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Stats/Stats.test.tsx +44 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Stats/Stats.tsx +39 -0
- package/dist/templates/with-reference-ds/design-system/src/components/Stats/index.ts +2 -0
- package/dist/templates/with-reference-ds/design-system/src/css-modules.d.ts +5 -0
- package/dist/templates/with-reference-ds/design-system/src/foundations/Colors.stories.tsx +46 -0
- package/dist/templates/with-reference-ds/design-system/src/foundations/Elevation.stories.tsx +37 -0
- package/dist/templates/with-reference-ds/design-system/src/foundations/LayoutContainers.stories.tsx +86 -0
- package/dist/templates/with-reference-ds/design-system/src/foundations/Radii.stories.tsx +38 -0
- package/dist/templates/with-reference-ds/design-system/src/foundations/Spacing.stories.tsx +33 -0
- package/dist/templates/with-reference-ds/design-system/src/foundations/Typography.stories.tsx +55 -0
- package/dist/templates/with-reference-ds/design-system/src/foundations/_helpers.tsx +82 -0
- package/dist/templates/with-reference-ds/design-system/src/index.ts +14 -0
- package/dist/templates/with-reference-ds/design-system/src/styles/base.css +55 -0
- package/dist/templates/with-reference-ds/design-system/src/styles/reset.css +48 -0
- package/dist/templates/with-reference-ds/design-system/src/styles/tokens.css +116 -0
- package/dist/templates/with-reference-ds/design-system/src/tokens/colors.ts +54 -0
- package/dist/templates/with-reference-ds/design-system/src/tokens/index.ts +32 -0
- package/dist/templates/with-reference-ds/design-system/src/tokens/layout.ts +26 -0
- package/dist/templates/with-reference-ds/design-system/src/tokens/radii.ts +11 -0
- package/dist/templates/with-reference-ds/design-system/src/tokens/spacing.ts +26 -0
- package/dist/templates/with-reference-ds/design-system/src/tokens/typography.ts +32 -0
- package/dist/templates/with-reference-ds/design-system/tsconfig.json +24 -0
- package/dist/templates/with-reference-ds/design-system/vitest.config.ts +15 -0
- package/dist/templates/with-reference-ds/design-system/vitest.setup.ts +7 -0
- package/dist/templates/without-reference-ds/design-system/.storybook/main.ts +21 -0
- package/dist/templates/without-reference-ds/design-system/.storybook/preview.ts +16 -0
- package/dist/templates/without-reference-ds/design-system/README.md +20 -0
- package/dist/templates/without-reference-ds/design-system/gitignore +5 -0
- package/dist/templates/without-reference-ds/design-system/package.json.template +33 -0
- package/dist/templates/without-reference-ds/design-system/src/foundations/Welcome.stories.tsx +24 -0
- package/dist/templates/without-reference-ds/design-system/src/index.ts +9 -0
- package/dist/templates/without-reference-ds/design-system/tsconfig.json +11 -0
- package/dist/utils/versions.d.ts +20 -0
- package/dist/utils/versions.d.ts.map +1 -0
- package/dist/utils/versions.js +59 -0
- package/dist/utils/versions.js.map +1 -0
- package/dist/versions.json +14 -0
- package/package.json +7 -5
- package/dist/templates/with-reference-ds/packages/design-system/package.json.template +0 -18
- package/dist/templates/with-reference-ds/packages/design-system/src/index.ts +0 -6
- package/dist/templates/with-reference-ds/packages/design-system/src/manifest.ts +0 -6
- package/dist/templates/with-reference-ds/packages/design-system/tsconfig.json +0 -9
- package/dist/templates/without-reference-ds/packages/design-system/README.md +0 -19
- package/dist/templates/without-reference-ds/packages/design-system/package.json.template +0 -14
- package/dist/templates/without-reference-ds/packages/design-system/src/index.ts +0 -8
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { ComponentContract } from '@forumone/throughline-design-contract'
|
|
2
|
+
|
|
3
|
+
export const contract: ComponentContract = {
|
|
4
|
+
name: 'Stats',
|
|
5
|
+
category: 'data',
|
|
6
|
+
description:
|
|
7
|
+
'A numerical data display with two to four stat items, each with a value, a label, and an optional description.',
|
|
8
|
+
intent:
|
|
9
|
+
'Surface a small set of headline metrics in a scannable row. Use for impact reports, program highlights, and annual summaries where numbers tell the story. Not appropriate for dense tabular data — that needs a real table component.',
|
|
10
|
+
|
|
11
|
+
composition: {
|
|
12
|
+
placement: ['section'],
|
|
13
|
+
maxPerPage: null,
|
|
14
|
+
requiredSiblings: [],
|
|
15
|
+
forbiddenAdjacent: [],
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
content: {
|
|
19
|
+
fields: [
|
|
20
|
+
{
|
|
21
|
+
name: 'eyebrow',
|
|
22
|
+
type: 'text',
|
|
23
|
+
required: false,
|
|
24
|
+
maxLength: 40,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: 'headline',
|
|
28
|
+
type: 'text',
|
|
29
|
+
required: false,
|
|
30
|
+
maxLength: 100,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: 'items',
|
|
34
|
+
type: 'array',
|
|
35
|
+
required: true,
|
|
36
|
+
constraints: 'Two to four items',
|
|
37
|
+
of: [
|
|
38
|
+
{ name: 'value', type: 'text', required: true, maxLength: 20 },
|
|
39
|
+
{ name: 'label', type: 'text', required: true, maxLength: 60 },
|
|
40
|
+
{ name: 'description', type: 'text', required: false, maxLength: 80 },
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
tokens: {
|
|
47
|
+
consumes: [
|
|
48
|
+
'color.text.primary',
|
|
49
|
+
'color.text.secondary',
|
|
50
|
+
'color.brand.primary',
|
|
51
|
+
'spacing.16',
|
|
52
|
+
'spacing.10',
|
|
53
|
+
'spacing.8',
|
|
54
|
+
'spacing.3',
|
|
55
|
+
'spacing.2',
|
|
56
|
+
'spacing.1',
|
|
57
|
+
'spacing.container',
|
|
58
|
+
'font.size.5xl',
|
|
59
|
+
'font.size.3xl',
|
|
60
|
+
'font.size.sm',
|
|
61
|
+
'font.weight.bold',
|
|
62
|
+
'font.weight.semibold',
|
|
63
|
+
'line.height.tight',
|
|
64
|
+
'letter.spacing.wide',
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
accessibility: {
|
|
69
|
+
keyboardSupport: [],
|
|
70
|
+
screenReaderBehavior:
|
|
71
|
+
'Statistics are rendered as a <dl> with <dt>/<dd> pairs. Screen readers traverse each value + label + description as a definition list entry.',
|
|
72
|
+
contentWarnings: [
|
|
73
|
+
'Keep values concise — "4,200" instead of "4,200,000,000 dollars"',
|
|
74
|
+
'Never invent numbers to fill the grid; use the real count even if unequal',
|
|
75
|
+
],
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
examples: [
|
|
79
|
+
{ label: 'Three stats', intent: 'Annual report highlights', storyId: 'stats--three-stats' },
|
|
80
|
+
{ label: 'Two stats', intent: 'Minimal impact summary', storyId: 'stats--two-stats' },
|
|
81
|
+
{ label: 'Four stats', intent: 'Dense overview', storyId: 'stats--four-stats' },
|
|
82
|
+
],
|
|
83
|
+
|
|
84
|
+
antiExamples: [
|
|
85
|
+
{
|
|
86
|
+
label: 'Five or more stats',
|
|
87
|
+
why: 'Beyond four items the layout cramps on mobile and the individual numbers lose impact',
|
|
88
|
+
useInstead: 'Pick the top four; move the rest to a full report page',
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
label: 'Paragraph-length values',
|
|
92
|
+
why: 'Stats are designed for at-a-glance reading; long values overwhelm the layout',
|
|
93
|
+
useInstead: 'Use Prose for extended narrative',
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
|
|
97
|
+
behavior: {
|
|
98
|
+
fetchesData: false,
|
|
99
|
+
hasClientState: false,
|
|
100
|
+
animates: false,
|
|
101
|
+
requiresAnalytics: false,
|
|
102
|
+
},
|
|
103
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
.stats {
|
|
2
|
+
padding: var(--spacing-16) 0;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.container {
|
|
6
|
+
max-width: 72rem;
|
|
7
|
+
margin: 0 auto;
|
|
8
|
+
padding: 0 var(--spacing-container);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.eyebrow {
|
|
12
|
+
font-size: var(--font-size-sm);
|
|
13
|
+
font-weight: var(--font-weight-semibold);
|
|
14
|
+
text-transform: uppercase;
|
|
15
|
+
letter-spacing: var(--letter-spacing-wide);
|
|
16
|
+
color: var(--color-text-secondary);
|
|
17
|
+
margin-bottom: var(--spacing-3);
|
|
18
|
+
text-align: center;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.headline {
|
|
22
|
+
font-size: var(--font-size-3xl);
|
|
23
|
+
font-weight: var(--font-weight-semibold);
|
|
24
|
+
margin-bottom: var(--spacing-10);
|
|
25
|
+
text-align: center;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.list {
|
|
29
|
+
display: grid;
|
|
30
|
+
gap: var(--spacing-8);
|
|
31
|
+
margin: 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.cols-2 {
|
|
35
|
+
grid-template-columns: repeat(2, 1fr);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.cols-3 {
|
|
39
|
+
grid-template-columns: repeat(3, 1fr);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.cols-4 {
|
|
43
|
+
grid-template-columns: repeat(4, 1fr);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@media (max-width: 720px) {
|
|
47
|
+
.cols-2,
|
|
48
|
+
.cols-3,
|
|
49
|
+
.cols-4 {
|
|
50
|
+
grid-template-columns: repeat(2, 1fr);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@media (max-width: 480px) {
|
|
55
|
+
.cols-2,
|
|
56
|
+
.cols-3,
|
|
57
|
+
.cols-4 {
|
|
58
|
+
grid-template-columns: 1fr;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.item {
|
|
63
|
+
text-align: center;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.value {
|
|
67
|
+
font-size: var(--font-size-5xl);
|
|
68
|
+
font-weight: var(--font-weight-bold);
|
|
69
|
+
line-height: var(--line-height-tight);
|
|
70
|
+
color: var(--color-brand-primary);
|
|
71
|
+
margin: 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.label {
|
|
75
|
+
margin: var(--spacing-2) 0 0;
|
|
76
|
+
display: flex;
|
|
77
|
+
flex-direction: column;
|
|
78
|
+
gap: var(--spacing-1);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.labelText {
|
|
82
|
+
font-weight: var(--font-weight-semibold);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.description {
|
|
86
|
+
font-size: var(--font-size-sm);
|
|
87
|
+
color: var(--color-text-secondary);
|
|
88
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import { Stats } from './Stats.js'
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof Stats> = {
|
|
5
|
+
title: 'Stats',
|
|
6
|
+
component: Stats,
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export default meta
|
|
10
|
+
|
|
11
|
+
type Story = StoryObj<typeof Stats>
|
|
12
|
+
|
|
13
|
+
export const ThreeStats: Story = {
|
|
14
|
+
args: {
|
|
15
|
+
eyebrow: 'Impact',
|
|
16
|
+
headline: 'Year in review',
|
|
17
|
+
items: [
|
|
18
|
+
{ value: '128', label: 'Grants awarded', description: 'Across twelve programs' },
|
|
19
|
+
{ value: '$4.2M', label: 'Total funded' },
|
|
20
|
+
{ value: '87%', label: 'Completion rate', description: 'Measured over five years' },
|
|
21
|
+
],
|
|
22
|
+
},
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const TwoStats: Story = {
|
|
26
|
+
args: {
|
|
27
|
+
items: [
|
|
28
|
+
{ value: '30', label: 'Partner organizations' },
|
|
29
|
+
{ value: '1,200', label: 'Researchers supported' },
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const FourStats: Story = {
|
|
35
|
+
args: {
|
|
36
|
+
headline: 'By the numbers',
|
|
37
|
+
items: [
|
|
38
|
+
{ value: '12', label: 'Countries' },
|
|
39
|
+
{ value: '48', label: 'Programs' },
|
|
40
|
+
{ value: '300+', label: 'Publications' },
|
|
41
|
+
{ value: '5M', label: 'People reached' },
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react'
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
import { Stats } from './Stats.js'
|
|
4
|
+
|
|
5
|
+
describe('Stats', () => {
|
|
6
|
+
it('renders each item as a dt/dd pair inside a dl', () => {
|
|
7
|
+
const { container } = render(
|
|
8
|
+
<Stats
|
|
9
|
+
items={[
|
|
10
|
+
{ value: '100', label: 'Projects' },
|
|
11
|
+
{ value: '50', label: 'Partners' },
|
|
12
|
+
]}
|
|
13
|
+
/>,
|
|
14
|
+
)
|
|
15
|
+
expect(container.querySelector('dl')).not.toBeNull()
|
|
16
|
+
const dts = container.querySelectorAll('dt')
|
|
17
|
+
const dds = container.querySelectorAll('dd')
|
|
18
|
+
expect(dts).toHaveLength(2)
|
|
19
|
+
expect(dds).toHaveLength(2)
|
|
20
|
+
expect(dts[0]).toHaveTextContent('100')
|
|
21
|
+
expect(dds[0]).toHaveTextContent('Projects')
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('renders optional eyebrow + headline', () => {
|
|
25
|
+
render(
|
|
26
|
+
<Stats
|
|
27
|
+
eyebrow="Impact"
|
|
28
|
+
headline="Summary"
|
|
29
|
+
items={[{ value: '1', label: 'Thing' }]}
|
|
30
|
+
/>,
|
|
31
|
+
)
|
|
32
|
+
expect(screen.getByText('Impact')).toBeInTheDocument()
|
|
33
|
+
expect(screen.getByRole('heading', { level: 2 })).toHaveTextContent('Summary')
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('renders item description when present', () => {
|
|
37
|
+
render(
|
|
38
|
+
<Stats
|
|
39
|
+
items={[{ value: '1', label: 'Thing', description: 'Extra context' }]}
|
|
40
|
+
/>,
|
|
41
|
+
)
|
|
42
|
+
expect(screen.getByText('Extra context')).toBeInTheDocument()
|
|
43
|
+
})
|
|
44
|
+
})
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { clsx } from 'clsx'
|
|
2
|
+
import styles from './Stats.module.css'
|
|
3
|
+
|
|
4
|
+
export interface StatItem {
|
|
5
|
+
value: string
|
|
6
|
+
label: string
|
|
7
|
+
description?: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface StatsProps {
|
|
11
|
+
eyebrow?: string
|
|
12
|
+
headline?: string
|
|
13
|
+
items: StatItem[]
|
|
14
|
+
className?: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function Stats({ eyebrow, headline, items, className }: StatsProps) {
|
|
18
|
+
return (
|
|
19
|
+
<section className={clsx(styles.stats, className)}>
|
|
20
|
+
<div className={styles.container}>
|
|
21
|
+
{eyebrow ? <p className={styles.eyebrow}>{eyebrow}</p> : null}
|
|
22
|
+
{headline ? <h2 className={styles.headline}>{headline}</h2> : null}
|
|
23
|
+
<dl className={clsx(styles.list, styles[`cols-${items.length}`])}>
|
|
24
|
+
{items.map((item) => (
|
|
25
|
+
<div key={`${item.value}-${item.label}`} className={styles.item}>
|
|
26
|
+
<dt className={styles.value}>{item.value}</dt>
|
|
27
|
+
<dd className={styles.label}>
|
|
28
|
+
<span className={styles.labelText}>{item.label}</span>
|
|
29
|
+
{item.description ? (
|
|
30
|
+
<span className={styles.description}>{item.description}</span>
|
|
31
|
+
) : null}
|
|
32
|
+
</dd>
|
|
33
|
+
</div>
|
|
34
|
+
))}
|
|
35
|
+
</dl>
|
|
36
|
+
</div>
|
|
37
|
+
</section>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import { colors } from '../tokens'
|
|
3
|
+
import { FoundationPage } from './_helpers.tsx'
|
|
4
|
+
|
|
5
|
+
const meta: Meta = {
|
|
6
|
+
title: 'Foundations/Colors',
|
|
7
|
+
}
|
|
8
|
+
export default meta
|
|
9
|
+
|
|
10
|
+
function cssVar(name: string): string {
|
|
11
|
+
return `--${name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase().replace(/\./g, '-')}`
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const groups = ['neutral', 'brand', 'text', 'bg', 'border', 'state']
|
|
15
|
+
|
|
16
|
+
export const Colors: StoryObj = {
|
|
17
|
+
render: () => (
|
|
18
|
+
<FoundationPage
|
|
19
|
+
title="Colors"
|
|
20
|
+
intro="The palette is grouped by role: neutral ramp, brand accents, text, background, border, and state. Components read these as CSS custom properties so a brand override re-themes everything without touching component code."
|
|
21
|
+
>
|
|
22
|
+
{groups.map((group) => {
|
|
23
|
+
const entries = Object.entries(colors).filter(([k]) => k.startsWith(`color.${group}.`))
|
|
24
|
+
if (entries.length === 0) return null
|
|
25
|
+
return (
|
|
26
|
+
<section key={group} style={{ marginBottom: '2rem' }}>
|
|
27
|
+
<h2 style={{ fontSize: 'var(--font-size-lg)', margin: '0 0 0.75rem', textTransform: 'capitalize' }}>
|
|
28
|
+
{group}
|
|
29
|
+
</h2>
|
|
30
|
+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(9rem, 1fr))', gap: '0.75rem' }}>
|
|
31
|
+
{entries.map(([name, value]) => (
|
|
32
|
+
<div key={name} style={{ border: '1px solid var(--color-border-default)', borderRadius: 'var(--radius-md)', overflow: 'hidden' }}>
|
|
33
|
+
<div style={{ height: '3.5rem', background: value }} />
|
|
34
|
+
<div style={{ padding: '0.5rem' }}>
|
|
35
|
+
<div style={{ fontFamily: 'var(--font-family-mono)', fontSize: 'var(--font-size-xs)' }}>{cssVar(name)}</div>
|
|
36
|
+
<div style={{ fontSize: 'var(--font-size-xs)', color: 'var(--color-text-muted)' }}>{value}</div>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
))}
|
|
40
|
+
</div>
|
|
41
|
+
</section>
|
|
42
|
+
)
|
|
43
|
+
})}
|
|
44
|
+
</FoundationPage>
|
|
45
|
+
),
|
|
46
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import { FoundationPage } from './_helpers.tsx'
|
|
3
|
+
|
|
4
|
+
const meta: Meta = {
|
|
5
|
+
title: 'Foundations/Elevation',
|
|
6
|
+
}
|
|
7
|
+
export default meta
|
|
8
|
+
|
|
9
|
+
export const Elevation: StoryObj = {
|
|
10
|
+
render: () => (
|
|
11
|
+
<FoundationPage
|
|
12
|
+
title="Elevation"
|
|
13
|
+
intro="The reference design system uses flat depth: elevation is expressed through 1px borders and surface-color shifts rather than drop shadows. Reserve real shadows for transient overlays (modals, popovers)."
|
|
14
|
+
>
|
|
15
|
+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(12rem, 1fr))', gap: '1rem' }}>
|
|
16
|
+
<div style={{ padding: '1.5rem', background: 'var(--color-bg-primary)', border: '1px solid var(--color-border-default)', borderRadius: 'var(--radius-lg)' }}>
|
|
17
|
+
<strong>Surface</strong>
|
|
18
|
+
<p style={{ margin: '0.5rem 0 0', color: 'var(--color-text-secondary)', fontSize: 'var(--font-size-sm)' }}>
|
|
19
|
+
bg.primary + border.default
|
|
20
|
+
</p>
|
|
21
|
+
</div>
|
|
22
|
+
<div style={{ padding: '1.5rem', background: 'var(--color-bg-secondary)', border: '1px solid var(--color-border-strong)', borderRadius: 'var(--radius-lg)' }}>
|
|
23
|
+
<strong>Raised</strong>
|
|
24
|
+
<p style={{ margin: '0.5rem 0 0', color: 'var(--color-text-secondary)', fontSize: 'var(--font-size-sm)' }}>
|
|
25
|
+
bg.secondary + border.strong
|
|
26
|
+
</p>
|
|
27
|
+
</div>
|
|
28
|
+
<div style={{ padding: '1.5rem', background: 'var(--color-bg-primary)', border: '1px solid var(--color-border-default)', borderRadius: 'var(--radius-lg)', boxShadow: '0 8px 24px rgba(0,0,0,0.12)' }}>
|
|
29
|
+
<strong>Overlay</strong>
|
|
30
|
+
<p style={{ margin: '0.5rem 0 0', color: 'var(--color-text-secondary)', fontSize: 'var(--font-size-sm)' }}>
|
|
31
|
+
shadow — modals / popovers only
|
|
32
|
+
</p>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
</FoundationPage>
|
|
36
|
+
),
|
|
37
|
+
}
|
package/dist/templates/with-reference-ds/design-system/src/foundations/LayoutContainers.stories.tsx
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import { layout } from '../tokens'
|
|
3
|
+
import { FoundationPage, TokenTable } from './_helpers.tsx'
|
|
4
|
+
|
|
5
|
+
const meta: Meta = {
|
|
6
|
+
title: 'Foundations/Layout & Containers',
|
|
7
|
+
}
|
|
8
|
+
export default meta
|
|
9
|
+
|
|
10
|
+
/** Token name → CSS custom property (mirrors build-tokens-css). */
|
|
11
|
+
function cssVar(name: string): string {
|
|
12
|
+
return `--${name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase().replace(/\./g, '-')}`
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const containers = Object.entries(layout).filter(([k]) => k.startsWith('layout.container.'))
|
|
16
|
+
const breakpoints = Object.entries(layout).filter(([k]) => k.startsWith('layout.breakpoint.'))
|
|
17
|
+
const gutter = layout['layout.gutter']
|
|
18
|
+
const margin = layout['layout.margin']
|
|
19
|
+
|
|
20
|
+
export const LayoutAndContainers: StoryObj = {
|
|
21
|
+
render: () => (
|
|
22
|
+
<FoundationPage
|
|
23
|
+
title="Layout & Containers"
|
|
24
|
+
intro="Page content constrains to one of a small set of max-widths. The gutter is the space between grid columns; the page margin is the breathing room between the container and the viewport edge. Breakpoints are min-width and progressively widen the layout."
|
|
25
|
+
>
|
|
26
|
+
<h2 style={{ fontSize: 'var(--font-size-xl)', margin: '0 0 1rem' }}>Container widths</h2>
|
|
27
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem', marginBottom: '2.5rem' }}>
|
|
28
|
+
{containers.map(([name, value]) => (
|
|
29
|
+
<div key={name}>
|
|
30
|
+
<div
|
|
31
|
+
style={{
|
|
32
|
+
fontFamily: 'var(--font-family-mono)',
|
|
33
|
+
fontSize: 'var(--font-size-xs)',
|
|
34
|
+
color: 'var(--color-text-secondary)',
|
|
35
|
+
marginBottom: '0.25rem',
|
|
36
|
+
}}
|
|
37
|
+
>
|
|
38
|
+
{cssVar(name)} · {value}
|
|
39
|
+
</div>
|
|
40
|
+
<div
|
|
41
|
+
style={{
|
|
42
|
+
height: '2rem',
|
|
43
|
+
width: `min(100%, ${value})`,
|
|
44
|
+
background: 'var(--color-brand-primary)',
|
|
45
|
+
borderRadius: 'var(--radius-sm)',
|
|
46
|
+
}}
|
|
47
|
+
/>
|
|
48
|
+
</div>
|
|
49
|
+
))}
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
<h2 style={{ fontSize: 'var(--font-size-xl)', margin: '0 0 1rem' }}>Gutter & page margin</h2>
|
|
53
|
+
<TokenTable
|
|
54
|
+
rows={[
|
|
55
|
+
{
|
|
56
|
+
name: cssVar('layout.gutter'),
|
|
57
|
+
value: gutter,
|
|
58
|
+
preview: <div style={{ height: '1rem', width: gutter, background: 'var(--color-brand-secondary)' }} />,
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: cssVar('layout.margin'),
|
|
62
|
+
value: margin,
|
|
63
|
+
preview: <div style={{ height: '1rem', width: margin, background: 'var(--color-brand-secondary)' }} />,
|
|
64
|
+
},
|
|
65
|
+
]}
|
|
66
|
+
/>
|
|
67
|
+
|
|
68
|
+
<h2 style={{ fontSize: 'var(--font-size-xl)', margin: '2.5rem 0 1rem' }}>Breakpoints</h2>
|
|
69
|
+
<TokenTable
|
|
70
|
+
rows={breakpoints.map(([name, value]) => ({
|
|
71
|
+
name: cssVar(name),
|
|
72
|
+
value,
|
|
73
|
+
preview: (
|
|
74
|
+
<div
|
|
75
|
+
style={{
|
|
76
|
+
height: '0.5rem',
|
|
77
|
+
width: `min(100%, calc(${value} / 4))`,
|
|
78
|
+
background: 'var(--color-border-strong)',
|
|
79
|
+
}}
|
|
80
|
+
/>
|
|
81
|
+
),
|
|
82
|
+
}))}
|
|
83
|
+
/>
|
|
84
|
+
</FoundationPage>
|
|
85
|
+
),
|
|
86
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import { radii } from '../tokens'
|
|
3
|
+
import { FoundationPage } from './_helpers.tsx'
|
|
4
|
+
|
|
5
|
+
const meta: Meta = {
|
|
6
|
+
title: 'Foundations/Radii',
|
|
7
|
+
}
|
|
8
|
+
export default meta
|
|
9
|
+
|
|
10
|
+
function cssVar(name: string): string {
|
|
11
|
+
return `--${name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase().replace(/\./g, '-')}`
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const Radii: StoryObj = {
|
|
15
|
+
render: () => (
|
|
16
|
+
<FoundationPage
|
|
17
|
+
title="Radii"
|
|
18
|
+
intro="Corner radii available to components. A brand may collapse this to a binary set (sharp vs. pill); the tokens stay the same so components don't change."
|
|
19
|
+
>
|
|
20
|
+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(10rem, 1fr))', gap: '1rem' }}>
|
|
21
|
+
{Object.entries(radii).map(([name, value]) => (
|
|
22
|
+
<div key={name}>
|
|
23
|
+
<div
|
|
24
|
+
style={{
|
|
25
|
+
height: '5rem',
|
|
26
|
+
background: 'var(--color-bg-tertiary)',
|
|
27
|
+
border: '1px solid var(--color-border-strong)',
|
|
28
|
+
borderRadius: value,
|
|
29
|
+
}}
|
|
30
|
+
/>
|
|
31
|
+
<div style={{ fontFamily: 'var(--font-family-mono)', fontSize: 'var(--font-size-xs)', marginTop: '0.5rem' }}>{cssVar(name)}</div>
|
|
32
|
+
<div style={{ fontSize: 'var(--font-size-xs)', color: 'var(--color-text-muted)' }}>{value}</div>
|
|
33
|
+
</div>
|
|
34
|
+
))}
|
|
35
|
+
</div>
|
|
36
|
+
</FoundationPage>
|
|
37
|
+
),
|
|
38
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import { spacing } from '../tokens'
|
|
3
|
+
import { FoundationPage } from './_helpers.tsx'
|
|
4
|
+
|
|
5
|
+
const meta: Meta = {
|
|
6
|
+
title: 'Foundations/Spacing',
|
|
7
|
+
}
|
|
8
|
+
export default meta
|
|
9
|
+
|
|
10
|
+
function cssVar(name: string): string {
|
|
11
|
+
return `--${name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase().replace(/\./g, '-')}`
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const Spacing: StoryObj = {
|
|
15
|
+
render: () => (
|
|
16
|
+
<FoundationPage
|
|
17
|
+
title="Spacing"
|
|
18
|
+
intro="A single spacing scale drives padding, margins, and gaps. Section and container spacing are semantic aliases on top of the numeric scale."
|
|
19
|
+
>
|
|
20
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
|
|
21
|
+
{Object.entries(spacing).map(([name, value]) => (
|
|
22
|
+
<div key={name} style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
|
|
23
|
+
<span style={{ fontFamily: 'var(--font-family-mono)', fontSize: 'var(--font-size-xs)', color: 'var(--color-text-muted)', width: '11rem', flexShrink: 0 }}>
|
|
24
|
+
{cssVar(name)}
|
|
25
|
+
</span>
|
|
26
|
+
<span style={{ fontSize: 'var(--font-size-xs)', color: 'var(--color-text-secondary)', width: '4rem', flexShrink: 0 }}>{value}</span>
|
|
27
|
+
<div style={{ height: '1rem', width: value, background: 'var(--color-brand-primary)', borderRadius: 'var(--radius-sm)' }} />
|
|
28
|
+
</div>
|
|
29
|
+
))}
|
|
30
|
+
</div>
|
|
31
|
+
</FoundationPage>
|
|
32
|
+
),
|
|
33
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import { typography } from '../tokens'
|
|
3
|
+
import { FoundationPage, TokenTable } from './_helpers.tsx'
|
|
4
|
+
|
|
5
|
+
const meta: Meta = {
|
|
6
|
+
title: 'Foundations/Typography',
|
|
7
|
+
}
|
|
8
|
+
export default meta
|
|
9
|
+
|
|
10
|
+
function cssVar(name: string): string {
|
|
11
|
+
return `--${name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase().replace(/\./g, '-')}`
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const sizes = Object.entries(typography).filter(([k]) => k.startsWith('font.size.'))
|
|
15
|
+
const families = Object.entries(typography).filter(([k]) => k.startsWith('font.family.'))
|
|
16
|
+
const weights = Object.entries(typography).filter(([k]) => k.startsWith('font.weight.'))
|
|
17
|
+
|
|
18
|
+
export const Typography: StoryObj = {
|
|
19
|
+
render: () => (
|
|
20
|
+
<FoundationPage
|
|
21
|
+
title="Typography"
|
|
22
|
+
intro="Type sizes form a single scale; hierarchy comes from size and weight, not from mixing typefaces. The font family is a token, so a brand swap (e.g. DM Sans) re-themes every component."
|
|
23
|
+
>
|
|
24
|
+
<h2 style={{ fontSize: 'var(--font-size-xl)', margin: '0 0 1rem' }}>Type scale</h2>
|
|
25
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem', marginBottom: '2.5rem' }}>
|
|
26
|
+
{sizes.map(([name, value]) => (
|
|
27
|
+
<div key={name} style={{ display: 'flex', alignItems: 'baseline', gap: '1rem' }}>
|
|
28
|
+
<span style={{ fontFamily: 'var(--font-family-mono)', fontSize: 'var(--font-size-xs)', color: 'var(--color-text-muted)', width: '8rem', flexShrink: 0 }}>
|
|
29
|
+
{cssVar(name)}
|
|
30
|
+
</span>
|
|
31
|
+
<span style={{ fontSize: value, lineHeight: 1.1 }}>The quick brown fox</span>
|
|
32
|
+
</div>
|
|
33
|
+
))}
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<h2 style={{ fontSize: 'var(--font-size-xl)', margin: '0 0 1rem' }}>Families</h2>
|
|
37
|
+
<TokenTable
|
|
38
|
+
rows={families.map(([name, value]) => ({
|
|
39
|
+
name: cssVar(name),
|
|
40
|
+
value,
|
|
41
|
+
preview: <span style={{ fontFamily: value }}>Aa Bb Cc 123</span>,
|
|
42
|
+
}))}
|
|
43
|
+
/>
|
|
44
|
+
|
|
45
|
+
<h2 style={{ fontSize: 'var(--font-size-xl)', margin: '2.5rem 0 1rem' }}>Weights</h2>
|
|
46
|
+
<TokenTable
|
|
47
|
+
rows={weights.map(([name, value]) => ({
|
|
48
|
+
name: cssVar(name),
|
|
49
|
+
value,
|
|
50
|
+
preview: <span style={{ fontWeight: value as number }}>The quick brown fox</span>,
|
|
51
|
+
}))}
|
|
52
|
+
/>
|
|
53
|
+
</FoundationPage>
|
|
54
|
+
),
|
|
55
|
+
}
|