create-backbone-template 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/README.md +33 -0
  2. package/bin/create-backbone-template.js +5 -0
  3. package/package.json +30 -0
  4. package/src/create-backbone-template.js +204 -0
  5. package/template/.agents/skills/agent-browser/SKILL.md +55 -0
  6. package/template/.agents/skills/create-plan/SKILL.md +52 -0
  7. package/template/.agents/skills/create-plan/agents/openai.yaml +4 -0
  8. package/template/.agents/skills/create-pr-presentation/SKILL.md +86 -0
  9. package/template/.agents/skills/create-pr-presentation/agents/openai.yaml +4 -0
  10. package/template/.agents/skills/implement-plan/SKILL.md +26 -0
  11. package/template/.agents/skills/implement-plan/agents/openai.yaml +4 -0
  12. package/template/.agents/skills/review-plan/SKILL.md +38 -0
  13. package/template/.agents/skills/review-plan/agents/openai.yaml +4 -0
  14. package/template/.env.schema +30 -0
  15. package/template/.env.test +6 -0
  16. package/template/.oxlintrc.json +67 -0
  17. package/template/.vscode/extensions.json +3 -0
  18. package/template/.vscode/settings.json +23 -0
  19. package/template/AGENTS.md +55 -0
  20. package/template/Cargo.lock +2648 -0
  21. package/template/Cargo.toml +29 -0
  22. package/template/Justfile +140 -0
  23. package/template/README.md +72 -0
  24. package/template/TODO.md +1 -0
  25. package/template/_gitignore +12 -0
  26. package/template/buf.gen.yaml +7 -0
  27. package/template/buf.yaml +10 -0
  28. package/template/client/.oxfmtrc.json +8 -0
  29. package/template/client/.oxlintrc.json +57 -0
  30. package/template/client/README.md +19 -0
  31. package/template/client/_gitignore +5 -0
  32. package/template/client/index.html +12 -0
  33. package/template/client/package.json +47 -0
  34. package/template/client/packages/design-system/package.json +19 -0
  35. package/template/client/packages/design-system/src/index.ts +2 -0
  36. package/template/client/packages/design-system-basic/package.json +18 -0
  37. package/template/client/packages/design-system-basic/src/button.stories.tsx +50 -0
  38. package/template/client/packages/design-system-basic/src/button.tsx +26 -0
  39. package/template/client/packages/design-system-basic/src/empty-state.stories.tsx +18 -0
  40. package/template/client/packages/design-system-basic/src/empty-state.tsx +17 -0
  41. package/template/client/packages/design-system-basic/src/form-field.stories.tsx +15 -0
  42. package/template/client/packages/design-system-basic/src/form-field.tsx +10 -0
  43. package/template/client/packages/design-system-basic/src/form.stories.tsx +27 -0
  44. package/template/client/packages/design-system-basic/src/form.tsx +9 -0
  45. package/template/client/packages/design-system-basic/src/heading.stories.tsx +14 -0
  46. package/template/client/packages/design-system-basic/src/heading.tsx +25 -0
  47. package/template/client/packages/design-system-basic/src/index.tsx +15 -0
  48. package/template/client/packages/design-system-basic/src/inline.stories.tsx +13 -0
  49. package/template/client/packages/design-system-basic/src/inline.tsx +5 -0
  50. package/template/client/packages/design-system-basic/src/layout.stories.tsx +24 -0
  51. package/template/client/packages/design-system-basic/src/layout.tsx +14 -0
  52. package/template/client/packages/design-system-basic/src/loader.stories.tsx +8 -0
  53. package/template/client/packages/design-system-basic/src/loader.tsx +11 -0
  54. package/template/client/packages/design-system-basic/src/navigation.stories.tsx +16 -0
  55. package/template/client/packages/design-system-basic/src/navigation.tsx +18 -0
  56. package/template/client/packages/design-system-basic/src/notice.stories.tsx +13 -0
  57. package/template/client/packages/design-system-basic/src/notice.tsx +5 -0
  58. package/template/client/packages/design-system-basic/src/stack.stories.tsx +17 -0
  59. package/template/client/packages/design-system-basic/src/stack.tsx +5 -0
  60. package/template/client/packages/design-system-basic/src/styles.css +254 -0
  61. package/template/client/packages/design-system-basic/src/text-input.stories.tsx +13 -0
  62. package/template/client/packages/design-system-basic/src/text-input.tsx +5 -0
  63. package/template/client/packages/design-system-basic/src/text.stories.tsx +21 -0
  64. package/template/client/packages/design-system-basic/src/text.tsx +5 -0
  65. package/template/client/packages/design-system-contract/package.json +15 -0
  66. package/template/client/packages/design-system-contract/src/button.ts +10 -0
  67. package/template/client/packages/design-system-contract/src/empty-state.ts +9 -0
  68. package/template/client/packages/design-system-contract/src/form-field.ts +9 -0
  69. package/template/client/packages/design-system-contract/src/form.ts +9 -0
  70. package/template/client/packages/design-system-contract/src/heading.ts +9 -0
  71. package/template/client/packages/design-system-contract/src/index.ts +13 -0
  72. package/template/client/packages/design-system-contract/src/inline.ts +7 -0
  73. package/template/client/packages/design-system-contract/src/layout.ts +8 -0
  74. package/template/client/packages/design-system-contract/src/loader.ts +7 -0
  75. package/template/client/packages/design-system-contract/src/navigation.ts +13 -0
  76. package/template/client/packages/design-system-contract/src/notice.ts +8 -0
  77. package/template/client/packages/design-system-contract/src/stack.ts +8 -0
  78. package/template/client/packages/design-system-contract/src/text-input.ts +5 -0
  79. package/template/client/packages/design-system-contract/src/text.ts +9 -0
  80. package/template/client/packages/design-system-lint/fixtures/invalid/external-ui-import.tsx +5 -0
  81. package/template/client/packages/design-system-lint/fixtures/invalid/raw-dom-jsx.tsx +3 -0
  82. package/template/client/packages/design-system-lint/fixtures/invalid/two-violations.tsx +7 -0
  83. package/template/client/packages/design-system-lint/fixtures/valid/design-system-only.tsx +13 -0
  84. package/template/client/packages/design-system-lint/package.json +23 -0
  85. package/template/client/packages/design-system-lint/src/check-design-system-architecture.ts +22 -0
  86. package/template/client/packages/design-system-lint/src/design-system-architecture.ts +286 -0
  87. package/template/client/packages/design-system-lint/src/oxlint-plugin.ts +11 -0
  88. package/template/client/packages/design-system-lint/src/page-architecture.ts +382 -0
  89. package/template/client/packages/design-system-lint/src/rules.ts +111 -0
  90. package/template/client/packages/design-system-lint/test/design-system-architecture.test.ts +243 -0
  91. package/template/client/packages/design-system-lint/test/oxlint-fixtures.test.ts +159 -0
  92. package/template/client/packages/design-system-lint/test/page-architecture.test.ts +175 -0
  93. package/template/client/packages/design-system-lint/test/rules.test.ts +65 -0
  94. package/template/client/packages/design-system-lint/tsconfig.json +29 -0
  95. package/template/client/src/App.tsx +77 -0
  96. package/template/client/src/design-system-components.test.tsx +75 -0
  97. package/template/client/src/gen/helloworld/v1/helloworld_pb.ts +63 -0
  98. package/template/client/src/main.tsx +18 -0
  99. package/template/client/src/pages/hello/hello-page.stories.tsx +20 -0
  100. package/template/client/src/pages/hello/hello-page.test.tsx +90 -0
  101. package/template/client/src/pages/hello/hello-page.tsx +126 -0
  102. package/template/client/src/pages/page.ts +20 -0
  103. package/template/client/src/testing/create-preview-events.test.ts +36 -0
  104. package/template/client/src/testing/create-preview-events.ts +30 -0
  105. package/template/client/src/vite-env.d.ts +1 -0
  106. package/template/client/tsconfig.json +32 -0
  107. package/template/client/vite.config.ts +21 -0
  108. package/template/client/vite.ladle.config.ts +5 -0
  109. package/template/e2e/.gherkin-lintrc +20 -0
  110. package/template/e2e/.oxfmtrc.json +15 -0
  111. package/template/e2e/.oxlintrc.json +37 -0
  112. package/template/e2e/_gitignore +4 -0
  113. package/template/e2e/features/helloworld.feature +10 -0
  114. package/template/e2e/package.json +42 -0
  115. package/template/e2e/playwright.config.ts +16 -0
  116. package/template/e2e/support/app-gherkin.ts +4 -0
  117. package/template/e2e/support/fixtures.ts +236 -0
  118. package/template/e2e/support/gherkin-fixtures/duplicate-id.feature +9 -0
  119. package/template/e2e/support/gherkin-fixtures/duplicate-id.spec.ts +7 -0
  120. package/template/e2e/support/gherkin-fixtures/extra-implementation.spec.ts +7 -0
  121. package/template/e2e/support/gherkin-fixtures/extra-step.spec.ts +10 -0
  122. package/template/e2e/support/gherkin-fixtures/happy-path.spec.ts +4 -0
  123. package/template/e2e/support/gherkin-fixtures/missing-id.feature +4 -0
  124. package/template/e2e/support/gherkin-fixtures/missing-id.spec.ts +7 -0
  125. package/template/e2e/support/gherkin-fixtures/missing-implementation.spec.ts +7 -0
  126. package/template/e2e/support/gherkin-fixtures/missing-step.spec.ts +7 -0
  127. package/template/e2e/support/gherkin-fixtures/playwright.config.ts +7 -0
  128. package/template/e2e/support/gherkin-fixtures/scenario-outline.feature +9 -0
  129. package/template/e2e/support/gherkin-fixtures/scenario-outline.spec.ts +7 -0
  130. package/template/e2e/support/gherkin-fixtures/step-mismatch.spec.ts +9 -0
  131. package/template/e2e/support/gherkin-fixtures/valid-implementations.ts +23 -0
  132. package/template/e2e/support/gherkin-fixtures/valid-scenarios.feature +26 -0
  133. package/template/e2e/support/gherkin.test.ts +184 -0
  134. package/template/e2e/support/gherkin.ts +321 -0
  135. package/template/e2e/support/oxlint-plugin.test.ts +328 -0
  136. package/template/e2e/support/oxlint-plugin.ts +485 -0
  137. package/template/e2e/tests/helloworld.spec.ts +39 -0
  138. package/template/e2e/tsconfig.json +26 -0
  139. package/template/e2e/tsconfig.oxlint-plugin.json +12 -0
  140. package/template/package.json +9 -0
  141. package/template/pnpm-lock.yaml +10723 -0
  142. package/template/pnpm-workspace.yaml +8 -0
  143. package/template/pr-slide/README.md +95 -0
  144. package/template/pr-slide/package.json +23 -0
  145. package/template/pr-slide/src/cli.js +262 -0
  146. package/template/pr-slide/src/generate-pr-deck.js +833 -0
  147. package/template/pr-slide/src/git-context.js +91 -0
  148. package/template/pr-slide/src/presentation-paths.js +9 -0
  149. package/template/pr-slide/src/presentations.js +53 -0
  150. package/template/pr-slide/test/generate-pr-deck.test.js +118 -0
  151. package/template/pr-slide/test/presentation-paths.test.js +14 -0
  152. package/template/pr-slide/test/presentations.test.js +50 -0
  153. package/template/proto/helloworld/v1/helloworld.proto +15 -0
  154. package/template/scripts/run-e2e.sh +10 -0
  155. package/template/server/Cargo.toml +26 -0
  156. package/template/server/build.rs +9 -0
  157. package/template/server/dylint/backbone_server_lints/.cargo/config.toml +6 -0
  158. package/template/server/dylint/backbone_server_lints/Cargo.lock +1581 -0
  159. package/template/server/dylint/backbone_server_lints/Cargo.toml +21 -0
  160. package/template/server/dylint/backbone_server_lints/README.md +5 -0
  161. package/template/server/dylint/backbone_server_lints/_gitignore +1 -0
  162. package/template/server/dylint/backbone_server_lints/rust-toolchain +3 -0
  163. package/template/server/dylint/backbone_server_lints/src/lib.rs +612 -0
  164. package/template/server/dylint/backbone_server_lints/ui/lib.rs +4 -0
  165. package/template/server/dylint/backbone_server_lints/ui/lib.stderr +10 -0
  166. package/template/server/dylint/backbone_server_lints/ui/long_file.rs +303 -0
  167. package/template/server/dylint/backbone_server_lints/ui/long_file.stderr +6 -0
  168. package/template/server/dylint/backbone_server_lints/ui/main.rs +59 -0
  169. package/template/server/dylint/backbone_server_lints/ui/main.stderr +85 -0
  170. package/template/server/migrations/20260520120000_create_projects.sql +12 -0
  171. package/template/server/migrations/20260524160000_create_hello_world_inputs.sql +12 -0
  172. package/template/server/src/config.rs +27 -0
  173. package/template/server/src/db/hello_world.rs +34 -0
  174. package/template/server/src/db/hello_world_tests.rs +11 -0
  175. package/template/server/src/db/mod.rs +39 -0
  176. package/template/server/src/lib.rs +10 -0
  177. package/template/server/src/main.rs +43 -0
  178. package/template/server/src/rpc/greeter/mod.rs +31 -0
  179. package/template/server/src/rpc/greeter/say_hello.rs +27 -0
  180. package/template/server/src/rpc/mod.rs +8 -0
  181. package/template/server/src/state.rs +13 -0
  182. package/template/skills-lock.json +11 -0
@@ -0,0 +1,14 @@
1
+ import type { Story, StoryDefault } from "@ladle/react"
2
+ import { Heading } from "."
3
+
4
+ export default {
5
+ title: "Design System / Heading",
6
+ } satisfies StoryDefault
7
+
8
+ export const Default: Story<{ children: string; level: 1 | 2 | 3 }> = ({ children, level }) => (
9
+ <Heading level={level}>{children}</Heading>
10
+ )
11
+ Default.args = {
12
+ children: "Heading",
13
+ level: 1,
14
+ }
@@ -0,0 +1,25 @@
1
+ import type { HeadingComponent } from "@backbone/design-system-contract"
2
+
3
+ export const Heading: HeadingComponent = ({ children, id, level = 1 }) => {
4
+ if (level === 2) {
5
+ return (
6
+ <h2 className="ds-heading ds-heading--2" id={id}>
7
+ {children}
8
+ </h2>
9
+ )
10
+ }
11
+
12
+ if (level === 3) {
13
+ return (
14
+ <h3 className="ds-heading ds-heading--3" id={id}>
15
+ {children}
16
+ </h3>
17
+ )
18
+ }
19
+
20
+ return (
21
+ <h1 className="ds-heading ds-heading--1" id={id}>
22
+ {children}
23
+ </h1>
24
+ )
25
+ }
@@ -0,0 +1,15 @@
1
+ import "./styles.css"
2
+
3
+ export * from "./button"
4
+ export * from "./empty-state"
5
+ export * from "./form"
6
+ export * from "./form-field"
7
+ export * from "./heading"
8
+ export * from "./inline"
9
+ export * from "./layout"
10
+ export * from "./loader"
11
+ export * from "./navigation"
12
+ export * from "./notice"
13
+ export * from "./stack"
14
+ export * from "./text"
15
+ export * from "./text-input"
@@ -0,0 +1,13 @@
1
+ import type { Story, StoryDefault } from "@ladle/react"
2
+ import { Button, Inline, TextInput } from "."
3
+
4
+ export default {
5
+ title: "Design System / Inline",
6
+ } satisfies StoryDefault
7
+
8
+ export const Default: Story = () => (
9
+ <Inline>
10
+ <TextInput aria-label="Inline input" placeholder="Inline input" />
11
+ <Button>Action</Button>
12
+ </Inline>
13
+ )
@@ -0,0 +1,5 @@
1
+ import type { InlineComponent } from "@backbone/design-system-contract"
2
+
3
+ export const Inline: InlineComponent = ({ children }) => {
4
+ return <div className="ds-inline">{children}</div>
5
+ }
@@ -0,0 +1,24 @@
1
+ import type { Story, StoryDefault } from "@ladle/react"
2
+ import { Heading, Layout, Stack, Text } from "."
3
+
4
+ export default {
5
+ title: "Design System / Layout",
6
+ } satisfies StoryDefault
7
+
8
+ export const Default: Story<{ middleWidthPx: number }> = ({ middleWidthPx }) => (
9
+ <Layout
10
+ middle={
11
+ <Stack gap="md">
12
+ <Text variant="eyebrow">Layout</Text>
13
+ <Heading level={2}>Centered content column</Heading>
14
+ <Text tone="muted">
15
+ The layout component centers one primary content region and constrains its width.
16
+ </Text>
17
+ </Stack>
18
+ }
19
+ middleWidthPx={middleWidthPx}
20
+ />
21
+ )
22
+ Default.args = {
23
+ middleWidthPx: 680,
24
+ }
@@ -0,0 +1,14 @@
1
+ import type { LayoutComponent } from "@backbone/design-system-contract"
2
+ import type { CSSProperties } from "react"
3
+
4
+ export const Layout: LayoutComponent = ({ middle, middleWidthPx = 680 }) => {
5
+ const middleStyle = { "--ds-middle-width": `${middleWidthPx}px` } as CSSProperties
6
+
7
+ return (
8
+ <main className="ds-layout">
9
+ <section className="ds-layout__middle" style={middleStyle}>
10
+ {middle}
11
+ </section>
12
+ </main>
13
+ )
14
+ }
@@ -0,0 +1,8 @@
1
+ import type { StoryDefault } from "@ladle/react"
2
+ import { Loader } from "."
3
+
4
+ export default {
5
+ title: "Design System / Loader",
6
+ } satisfies StoryDefault
7
+
8
+ export const Default = () => <Loader message="Loading hello-world inputs..." />
@@ -0,0 +1,11 @@
1
+ import type { LoaderComponent } from "@backbone/design-system-contract"
2
+ import { Text } from "./text"
3
+
4
+ export const Loader: LoaderComponent = ({ message }) => {
5
+ return (
6
+ <section aria-busy="true" aria-live="polite" className="ds-loader">
7
+ <span className="ds-loader__spinner" />
8
+ <Text tone="muted">{message}</Text>
9
+ </section>
10
+ )
11
+ }
@@ -0,0 +1,16 @@
1
+ import type { StoryDefault } from "@ladle/react"
2
+ import { Navigation } from "."
3
+
4
+ export default {
5
+ title: "Design System / Navigation",
6
+ } satisfies StoryDefault
7
+
8
+ export const Default = () => (
9
+ <Navigation
10
+ currentHref="/hello"
11
+ items={[
12
+ { href: "/hello", label: "Hello" },
13
+ { href: "/history", label: "History" },
14
+ ]}
15
+ />
16
+ )
@@ -0,0 +1,18 @@
1
+ import type { NavigationComponent } from "@backbone/design-system-contract"
2
+
3
+ export const Navigation: NavigationComponent = ({ currentHref, items }) => {
4
+ return (
5
+ <nav aria-label="Primary navigation" className="ds-navigation">
6
+ {items.map((item) => (
7
+ <a
8
+ aria-current={item.href === currentHref ? "page" : undefined}
9
+ className="ds-navigation__link"
10
+ href={item.href}
11
+ key={item.href}
12
+ >
13
+ {item.label}
14
+ </a>
15
+ ))}
16
+ </nav>
17
+ )
18
+ }
@@ -0,0 +1,13 @@
1
+ import type { Story, StoryDefault } from "@ladle/react"
2
+ import { Notice } from "."
3
+
4
+ export default {
5
+ title: "Design System / Notice",
6
+ } satisfies StoryDefault
7
+
8
+ export const Default: Story<{ children: string }> = ({ children }) => (
9
+ <Notice tone="danger">{children}</Notice>
10
+ )
11
+ Default.args = {
12
+ children: "Something needs attention.",
13
+ }
@@ -0,0 +1,5 @@
1
+ import type { NoticeComponent } from "@backbone/design-system-contract"
2
+
3
+ export const Notice: NoticeComponent = ({ children, tone }) => {
4
+ return <p className={`ds-notice ds-notice--${tone}`}>{children}</p>
5
+ }
@@ -0,0 +1,17 @@
1
+ import type { Story, StoryDefault } from "@ladle/react"
2
+ import { Stack, Text } from "."
3
+
4
+ export default {
5
+ title: "Design System / Stack",
6
+ } satisfies StoryDefault
7
+
8
+ export const Default: Story<{ gap: "sm" | "md" | "lg" }> = ({ gap }) => (
9
+ <Stack gap={gap}>
10
+ <Text>First stacked item</Text>
11
+ <Text tone="muted">Second stacked item</Text>
12
+ <Text tone="accent">Third stacked item</Text>
13
+ </Stack>
14
+ )
15
+ Default.args = {
16
+ gap: "md",
17
+ }
@@ -0,0 +1,5 @@
1
+ import type { StackComponent } from "@backbone/design-system-contract"
2
+
3
+ export const Stack: StackComponent = ({ children, gap = "md" }) => {
4
+ return <div className={`ds-stack ds-stack--${gap}`}>{children}</div>
5
+ }
@@ -0,0 +1,254 @@
1
+ :root {
2
+ color: #172033;
3
+ background: #f7f9fc;
4
+ font-family:
5
+ Inter,
6
+ ui-sans-serif,
7
+ system-ui,
8
+ -apple-system,
9
+ BlinkMacSystemFont,
10
+ "Segoe UI",
11
+ sans-serif;
12
+ font-synthesis: none;
13
+ text-rendering: optimizeLegibility;
14
+ -webkit-font-smoothing: antialiased;
15
+ -moz-osx-font-smoothing: grayscale;
16
+ }
17
+
18
+ * {
19
+ box-sizing: border-box;
20
+ }
21
+
22
+ body {
23
+ min-width: 320px;
24
+ min-height: 100vh;
25
+ margin: 0;
26
+ }
27
+
28
+ .ds-navigation {
29
+ display: flex;
30
+ flex-wrap: wrap;
31
+ justify-content: center;
32
+ gap: 0.25rem;
33
+ border-bottom: 1px solid #d9e1ec;
34
+ background: #ffffff;
35
+ padding: 0.75rem 1rem;
36
+ }
37
+
38
+ .ds-navigation__link {
39
+ border-radius: 6px;
40
+ padding: 0.55rem 0.8rem;
41
+ color: #26354d;
42
+ font-weight: 700;
43
+ text-decoration: none;
44
+ }
45
+
46
+ .ds-navigation__link[aria-current="page"] {
47
+ background: #e9f2ef;
48
+ color: #245c51;
49
+ }
50
+
51
+ .ds-layout {
52
+ display: grid;
53
+ min-height: calc(100vh - 4rem);
54
+ place-items: center;
55
+ padding: 2rem;
56
+ }
57
+
58
+ .ds-layout__middle {
59
+ width: min(100%, var(--ds-middle-width));
60
+ }
61
+
62
+ .ds-stack {
63
+ display: grid;
64
+ }
65
+
66
+ .ds-stack--sm {
67
+ gap: 0.65rem;
68
+ }
69
+
70
+ .ds-stack--md {
71
+ gap: 1rem;
72
+ }
73
+
74
+ .ds-stack--lg {
75
+ gap: 1.5rem;
76
+ }
77
+
78
+ .ds-inline {
79
+ display: grid;
80
+ grid-template-columns: minmax(0, 1fr) auto;
81
+ gap: 0.75rem;
82
+ }
83
+
84
+ .ds-text {
85
+ margin: 0;
86
+ }
87
+
88
+ .ds-text--eyebrow {
89
+ color: #2f6f62;
90
+ font-size: 0.8rem;
91
+ font-weight: 700;
92
+ letter-spacing: 0.12em;
93
+ text-transform: uppercase;
94
+ }
95
+
96
+ .ds-text--lede {
97
+ max-width: 48rem;
98
+ color: #4d5c73;
99
+ font-size: 1.125rem;
100
+ line-height: 1.7;
101
+ }
102
+
103
+ .ds-text--body {
104
+ color: #172033;
105
+ line-height: 1.5;
106
+ }
107
+
108
+ .ds-text--muted {
109
+ color: #4d5c73;
110
+ }
111
+
112
+ .ds-text--accent {
113
+ color: #2f6f62;
114
+ }
115
+
116
+ .ds-text--danger {
117
+ color: #a23232;
118
+ }
119
+
120
+ .ds-heading {
121
+ margin: 0;
122
+ color: #0c1322;
123
+ }
124
+
125
+ .ds-heading--1 {
126
+ font-size: clamp(2.35rem, 8vw, 5rem);
127
+ line-height: 0.95;
128
+ }
129
+
130
+ .ds-heading--2 {
131
+ font-size: 2rem;
132
+ line-height: 1.05;
133
+ }
134
+
135
+ .ds-heading--3 {
136
+ font-size: 1.35rem;
137
+ line-height: 1.15;
138
+ }
139
+
140
+ .ds-form {
141
+ display: grid;
142
+ gap: 0.65rem;
143
+ max-width: 30rem;
144
+ margin-top: 0.5rem;
145
+ }
146
+
147
+ .ds-field {
148
+ display: grid;
149
+ gap: 0.65rem;
150
+ }
151
+
152
+ .ds-field__label {
153
+ color: #28364f;
154
+ font-size: 0.9rem;
155
+ font-weight: 700;
156
+ }
157
+
158
+ .ds-input,
159
+ .ds-button {
160
+ min-height: 2.75rem;
161
+ border-radius: 6px;
162
+ font: inherit;
163
+ }
164
+
165
+ .ds-input {
166
+ width: 100%;
167
+ border: 1px solid #c7d1df;
168
+ background: #ffffff;
169
+ padding: 0 0.8rem;
170
+ color: #172033;
171
+ }
172
+
173
+ .ds-button {
174
+ border: 0;
175
+ padding: 0 1rem;
176
+ font-weight: 700;
177
+ cursor: pointer;
178
+ }
179
+
180
+ .ds-button--primary {
181
+ background: #2f6f62;
182
+ color: #ffffff;
183
+ }
184
+
185
+ .ds-button--secondary {
186
+ border: 1px solid #b7c3d4;
187
+ background: #ffffff;
188
+ color: #26354d;
189
+ }
190
+
191
+ .ds-button--danger {
192
+ background: #a23232;
193
+ color: #ffffff;
194
+ }
195
+
196
+ .ds-button:disabled {
197
+ cursor: not-allowed;
198
+ opacity: 0.72;
199
+ }
200
+
201
+ .ds-button--loading:disabled {
202
+ cursor: progress;
203
+ }
204
+
205
+ .ds-empty-state {
206
+ display: grid;
207
+ gap: 1rem;
208
+ border: 1px dashed #b7c3d4;
209
+ border-radius: 8px;
210
+ padding: 1.25rem;
211
+ background: #ffffff;
212
+ }
213
+
214
+ .ds-empty-state__actions {
215
+ display: flex;
216
+ flex-wrap: wrap;
217
+ gap: 0.75rem;
218
+ }
219
+
220
+ .ds-loader {
221
+ display: flex;
222
+ align-items: center;
223
+ gap: 0.75rem;
224
+ min-height: 4rem;
225
+ }
226
+
227
+ .ds-loader__spinner {
228
+ width: 1.15rem;
229
+ height: 1.15rem;
230
+ border: 2px solid #c7d1df;
231
+ border-top-color: #2f6f62;
232
+ border-radius: 999px;
233
+ animation: ds-loader-spin 0.8s linear infinite;
234
+ }
235
+
236
+ @keyframes ds-loader-spin {
237
+ to {
238
+ transform: rotate(360deg);
239
+ }
240
+ }
241
+
242
+ .ds-notice {
243
+ margin: 0;
244
+ }
245
+
246
+ .ds-notice--danger {
247
+ color: #a23232;
248
+ }
249
+
250
+ @media (max-width: 520px) {
251
+ .ds-inline {
252
+ grid-template-columns: 1fr;
253
+ }
254
+ }
@@ -0,0 +1,13 @@
1
+ import type { Story, StoryDefault } from "@ladle/react"
2
+ import { TextInput } from "."
3
+
4
+ export default {
5
+ title: "Design System / TextInput",
6
+ } satisfies StoryDefault
7
+
8
+ export const Default: Story<{ placeholder: string }> = ({ placeholder }) => (
9
+ <TextInput aria-label="Text input" placeholder={placeholder} />
10
+ )
11
+ Default.args = {
12
+ placeholder: "World",
13
+ }
@@ -0,0 +1,5 @@
1
+ import type { TextInputComponent } from "@backbone/design-system-contract"
2
+
3
+ export const TextInput: TextInputComponent = (props) => {
4
+ return <input className="ds-input" {...props} />
5
+ }
@@ -0,0 +1,21 @@
1
+ import type { Story, StoryDefault } from "@ladle/react"
2
+ import { Text } from "."
3
+
4
+ export default {
5
+ title: "Design System / Text",
6
+ } satisfies StoryDefault
7
+
8
+ export const Default: Story<{
9
+ children: string
10
+ tone: "default" | "muted" | "accent" | "danger"
11
+ variant: "body" | "lede" | "eyebrow"
12
+ }> = ({ children, tone, variant }) => (
13
+ <Text tone={tone} variant={variant}>
14
+ {children}
15
+ </Text>
16
+ )
17
+ Default.args = {
18
+ children: "Design-system text supports body, lede, and eyebrow variants.",
19
+ tone: "default",
20
+ variant: "body",
21
+ }
@@ -0,0 +1,5 @@
1
+ import type { TextComponent } from "@backbone/design-system-contract"
2
+
3
+ export const Text: TextComponent = ({ children, tone = "default", variant = "body" }) => {
4
+ return <p className={`ds-text ds-text--${variant} ds-text--${tone}`}>{children}</p>
5
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "@backbone/design-system-contract",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./src/index.ts",
9
+ "default": "./src/index.ts"
10
+ }
11
+ },
12
+ "peerDependencies": {
13
+ "react": "^19.0.0"
14
+ }
15
+ }
@@ -0,0 +1,10 @@
1
+ import type { ButtonHTMLAttributes, ComponentType } from "react"
2
+
3
+ type ButtonVariant = "primary" | "secondary" | "danger"
4
+
5
+ export type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
6
+ loading?: boolean
7
+ variant?: ButtonVariant
8
+ }
9
+
10
+ export type ButtonComponent = ComponentType<ButtonProps>
@@ -0,0 +1,9 @@
1
+ import type { ComponentType, ReactNode } from "react"
2
+
3
+ export type EmptyStateProps = {
4
+ action?: ReactNode
5
+ description: string
6
+ title: string
7
+ }
8
+
9
+ export type EmptyStateComponent = ComponentType<EmptyStateProps>
@@ -0,0 +1,9 @@
1
+ import type { ComponentType, ReactNode } from "react"
2
+
3
+ export type FormFieldProps = {
4
+ children: ReactNode
5
+ inputId: string
6
+ label: ReactNode
7
+ }
8
+
9
+ export type FormFieldComponent = ComponentType<FormFieldProps>
@@ -0,0 +1,9 @@
1
+ import type { ComponentType, FormEventHandler, ReactNode } from "react"
2
+
3
+ export type FormProps = {
4
+ children: ReactNode
5
+ ariaLabelledBy?: string
6
+ onSubmit?: FormEventHandler<HTMLFormElement>
7
+ }
8
+
9
+ export type FormComponent = ComponentType<FormProps>
@@ -0,0 +1,9 @@
1
+ import type { ComponentType, ReactNode } from "react"
2
+
3
+ export type HeadingProps = {
4
+ children: ReactNode
5
+ id?: string
6
+ level?: 1 | 2 | 3
7
+ }
8
+
9
+ export type HeadingComponent = ComponentType<HeadingProps>
@@ -0,0 +1,13 @@
1
+ export type * from "./button"
2
+ export type * from "./empty-state"
3
+ export type * from "./form"
4
+ export type * from "./form-field"
5
+ export type * from "./heading"
6
+ export type * from "./inline"
7
+ export type * from "./layout"
8
+ export type * from "./loader"
9
+ export type * from "./navigation"
10
+ export type * from "./notice"
11
+ export type * from "./stack"
12
+ export type * from "./text"
13
+ export type * from "./text-input"
@@ -0,0 +1,7 @@
1
+ import type { ComponentType, ReactNode } from "react"
2
+
3
+ export type InlineProps = {
4
+ children: ReactNode
5
+ }
6
+
7
+ export type InlineComponent = ComponentType<InlineProps>
@@ -0,0 +1,8 @@
1
+ import type { ComponentType, ReactNode } from "react"
2
+
3
+ export type LayoutProps = {
4
+ middle: ReactNode
5
+ middleWidthPx?: number
6
+ }
7
+
8
+ export type LayoutComponent = ComponentType<LayoutProps>
@@ -0,0 +1,7 @@
1
+ import type { ComponentType } from "react"
2
+
3
+ export type LoaderProps = {
4
+ message: string
5
+ }
6
+
7
+ export type LoaderComponent = ComponentType<LoaderProps>
@@ -0,0 +1,13 @@
1
+ import type { ComponentType } from "react"
2
+
3
+ type NavigationItem = {
4
+ href: string
5
+ label: string
6
+ }
7
+
8
+ export type NavigationProps = {
9
+ currentHref?: string
10
+ items: NavigationItem[]
11
+ }
12
+
13
+ export type NavigationComponent = ComponentType<NavigationProps>
@@ -0,0 +1,8 @@
1
+ import type { ComponentType, ReactNode } from "react"
2
+
3
+ export type NoticeProps = {
4
+ children: ReactNode
5
+ tone: "danger"
6
+ }
7
+
8
+ export type NoticeComponent = ComponentType<NoticeProps>
@@ -0,0 +1,8 @@
1
+ import type { ComponentType, ReactNode } from "react"
2
+
3
+ export type StackProps = {
4
+ children: ReactNode
5
+ gap?: "sm" | "md" | "lg"
6
+ }
7
+
8
+ export type StackComponent = ComponentType<StackProps>